Commit 521deffe authored by Ridma Dilshan's avatar Ridma Dilshan

Fix Update

parent e319e76b
......@@ -4,5 +4,9 @@
"Janith",
"leaderboard",
"SLIIT"
]
],
"[python]": {
"editor.defaultFormatter": "ms-python.black-formatter"
},
"python.formatting.provider": "none"
}
\ No newline at end of file
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"id": "4a663198",
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"from PIL import Image\n",
"import numpy as np\n",
"from sklearn.model_selection import train_test_split\n",
"from sklearn.preprocessing import LabelEncoder\n",
"from tensorflow import keras\n",
"from tensorflow.keras import layers\n",
"from keras.models import load_model"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "6a893211",
"metadata": {},
"outputs": [],
"source": [
"# Set the dataset path and image size\n",
"dataset_path = \"C:/Users/himashara/Documents/signlanguage/training/\"\n",
"image_size = (480, 480) # Adjust the image size as per your requirements\n",
"\n",
"# Load and preprocess the dataset\n",
"def load_dataset():\n",
" labels = []\n",
" images = []\n",
" for label in os.listdir(dataset_path):\n",
" label_path = os.path.join(dataset_path, label)\n",
" if os.path.isdir(label_path):\n",
" for image_file in os.listdir(label_path):\n",
" image_path = os.path.join(label_path, image_file)\n",
" if os.path.isfile(image_path):\n",
" image = Image.open(image_path)\n",
" image = image.resize(image_size)\n",
" image = np.array(image)\n",
" images.append(image)\n",
" labels.append(label)\n",
" images = np.array(images)\n",
" labels = np.array(labels)\n",
" return images, labels\n",
"\n",
"images, labels = load_dataset()"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "ef0e4281",
"metadata": {},
"outputs": [],
"source": [
"# Encode the labels into numerical values\n",
"label_encoder = LabelEncoder()\n",
"labels = label_encoder.fit_transform(labels)\n",
"\n",
"# Split the dataset into training and testing sets\n",
"X_train, X_test, y_train, y_test = train_test_split(images, labels, test_size=0.2, random_state=42)\n",
"\n",
"# Preprocess the input data\n",
"X_train = X_train.astype(\"float32\") / 255.0\n",
"X_test = X_test.astype(\"float32\") / 255.0\n",
"num_classes = len(np.unique(labels))\n",
"y_train = keras.utils.to_categorical(y_train, num_classes)\n",
"y_test = keras.utils.to_categorical(y_test, num_classes)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "555ffeee",
"metadata": {},
"outputs": [],
"source": [
"# Define the model architecture\n",
"model = keras.Sequential([\n",
" layers.Conv2D(32, (3, 3), activation=\"relu\", input_shape=(image_size[0], image_size[1], 3)),\n",
" layers.MaxPooling2D(pool_size=(2, 2)),\n",
" layers.Flatten(),\n",
" layers.Dense(64, activation=\"relu\"),\n",
" layers.Dense(num_classes, activation=\"softmax\")\n",
"])"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "eb07946c",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Epoch 1/10\n",
"22/22 [==============================] - 58s 3s/step - loss: 58.0361 - accuracy: 0.1509 - val_loss: 2.3127 - val_accuracy: 0.2059\n",
"Epoch 2/10\n",
"22/22 [==============================] - 59s 3s/step - loss: 2.3554 - accuracy: 0.2278 - val_loss: 2.0756 - val_accuracy: 0.1824\n",
"Epoch 3/10\n",
"22/22 [==============================] - 67s 3s/step - loss: 2.0049 - accuracy: 0.3195 - val_loss: 2.0513 - val_accuracy: 0.2765\n",
"Epoch 4/10\n",
"22/22 [==============================] - 65s 3s/step - loss: 1.9182 - accuracy: 0.3580 - val_loss: 1.8208 - val_accuracy: 0.3235\n",
"Epoch 5/10\n",
"22/22 [==============================] - 87s 4s/step - loss: 1.7040 - accuracy: 0.3905 - val_loss: 1.7384 - val_accuracy: 0.3294\n",
"Epoch 6/10\n",
"22/22 [==============================] - 70s 3s/step - loss: 1.6378 - accuracy: 0.4024 - val_loss: 1.9270 - val_accuracy: 0.3059\n",
"Epoch 7/10\n",
"22/22 [==============================] - 71s 3s/step - loss: 1.6423 - accuracy: 0.3905 - val_loss: 1.7039 - val_accuracy: 0.3529\n",
"Epoch 8/10\n",
"22/22 [==============================] - 66s 3s/step - loss: 1.5635 - accuracy: 0.4083 - val_loss: 1.6209 - val_accuracy: 0.3294\n",
"Epoch 9/10\n",
"22/22 [==============================] - 67s 3s/step - loss: 1.4948 - accuracy: 0.4038 - val_loss: 1.5666 - val_accuracy: 0.3765\n",
"Epoch 10/10\n",
"22/22 [==============================] - 74s 3s/step - loss: 1.4630 - accuracy: 0.4615 - val_loss: 1.5821 - val_accuracy: 0.3588\n"
]
},
{
"data": {
"text/plain": [
"<keras.callbacks.History at 0x26f794884c0>"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Compile and train the model\n",
"model.compile(loss=\"categorical_crossentropy\", optimizer=\"adam\", metrics=[\"accuracy\"])\n",
"model.fit(X_train, y_train, batch_size=32, epochs=10, validation_data=(X_test, y_test))"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "81b6613d",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"6/6 [==============================] - 7s 1s/step - loss: 1.5821 - accuracy: 0.3588\n",
"Test Loss: 1.5821244716644287\n",
"Test Accuracy: 0.3588235378265381\n"
]
}
],
"source": [
"# Evaluate the model on the test dataset\n",
"loss, accuracy = model.evaluate(X_test, y_test)\n",
"print(\"Test Loss:\", loss)\n",
"print(\"Test Accuracy:\", accuracy)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "65ccc47b",
"metadata": {},
"outputs": [],
"source": [
"# Save the trained model\n",
"model.save(\"letter_classification_model.h5\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "0db64f31",
"metadata": {},
"outputs": [],
"source": [
"# ---------------new updated ---------------"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d0ce9c60",
"metadata": {},
"outputs": [],
"source": [
"# import os\n",
"# from PIL import Image\n",
"# import numpy as np\n",
"# from keras.models import load_model\n",
"# from sinling import SinhalaTokenizer\n",
"# from IPython.display import Image as DisplayImage, display\n",
"\n",
"# image_size = (480, 480)\n",
"\n",
"# # Dataset path\n",
"# dataset_path = \"C:/Users/himashara/Documents/signlanguage/training/\"\n",
"\n",
"# # Translation mapping from Sinhala Unicode to Singlish\n",
"# sinhala_to_singlish = {\n",
"# \"අ\": \"A\",\n",
"# \"ආ\": \"Aah\",\n",
"# \"ඇ\": \"Aeh\",\n",
"# \"ඉ\": \"Ee\",\n",
"# \"ඊ\": \"Eeh\",\n",
"# \"උ\": \"Uh\",\n",
"# \"ඌ\": \"Uhh\",\n",
"# \"එ\": \"A\",\n",
"# \"ඒ\": \"Ae\",\n",
"# \"ඔ\": \"O\",\n",
"# \"ඕ\": \"Ohh\",\n",
"# \"ක්\": \"K\",\n",
"# \"ග්\": \"Ig\",\n",
"# \"ටී\": \"Tee\",\n",
"# \"ට\": \"T\"\n",
"# }\n",
"\n",
"# # Function to retrieve the letter image\n",
"# def get_letter_image(letter):\n",
"# if os.path.exists(os.path.join(dataset_path, letter)):\n",
"# letter_path = os.path.join(dataset_path, letter)\n",
"# image_files = os.listdir(letter_path)\n",
"# image_path = os.path.join(letter_path, image_files[0]) # Assuming only one image per letter\n",
"# return Image.open(image_path)\n",
"# return None\n",
"\n",
"# # Load the pre-trained model\n",
"# model = load_model(\"letter_classification_model.h5\")\n",
"\n",
"# # Sinhala tokenizer\n",
"# tokenizer = SinhalaTokenizer()\n",
"\n",
"# # Function to preprocess Sinhala text\n",
"# def preprocess_sinhala_text(text):\n",
"# tokens = tokenizer.tokenize(text)\n",
"# return ' '.join(tokens)\n",
"\n",
"# # User input\n",
"# unicode_input = input(\"Enter a Sinhala Unicode string: \")\n",
"\n",
"# # Remove spaces from the user input\n",
"# unicode_input = unicode_input.replace(\" \", \"\")\n",
"\n",
"# # Translate Sinhala Unicode to Singlish\n",
"# singlish_input = ''.join([sinhala_to_singlish.get(c, c) for c in unicode_input])\n",
"\n",
"# # Print the translated Singlish string\n",
"# print(\"Translated Singlish string:\", singlish_input)\n",
"\n",
"# # Retrieve letter images for the Singlish input\n",
"# letter_images = []\n",
"# combined_string = \"\"\n",
"# for letter in singlish_input:\n",
"# combined_string += letter\n",
"# letter_image = get_letter_image(combined_string)\n",
"# if letter_image is not None:\n",
"# letter_image = letter_image.resize(image_size) # Adjust the size if needed\n",
"# letter_image = np.array(letter_image) / 255.0 # Normalize the image if needed\n",
"# letter_images.append(letter_image)\n",
"# combined_string = \"\"\n",
"\n",
"# # Combine the letter images into a GIF\n",
"# gif_images = [Image.fromarray(np.uint8(letter_image * 255)) for letter_image in letter_images]\n",
"\n",
"# # Save the combined images as a GIF\n",
"# output_path = \"C:/Users/himashara/Documents/signlanguage/generated-gif/generated.gif\"\n",
"# gif_images[0].save(output_path, save_all=True, append_images=gif_images[1:], loop=0, duration=1200)\n",
"\n",
"# # Display the generated GIF\n",
"# display(DisplayImage(filename=output_path))\n",
"# print(\"GIF saved successfully!\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ea87d3ca",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": 67,
"id": "fc22ffc8",
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"import tkinter as tk\n",
"from tkinter import ttk\n",
"from PIL import Image, ImageTk, ImageSequence\n",
"import numpy as np\n",
"from keras.models import load_model\n",
"from sinling import SinhalaTokenizer\n",
"\n",
"# Create Tkinter window\n",
"window = tk.Tk()\n",
"window.title(\"Sinhala to Singlish Translator\")\n",
"\n",
"# Set up the layout using Tkinter's grid system\n",
"# Step 1: Text area to enter Sinhala Unicode string\n",
"unicode_label = ttk.Label(window, text=\"Enter a Sinhala Unicode string:\")\n",
"unicode_label.grid(row=0, column=0, sticky=tk.W)\n",
"unicode_entry = ttk.Entry(window, width=30)\n",
"unicode_entry.grid(row=0, column=1, columnspan=2, pady=10)\n",
"\n",
"# Step 2: Convert button\n",
"def convert_unicode_to_singlish():\n",
" # Retrieve the Unicode input from the text entry\n",
" unicode_input = unicode_entry.get()\n",
"\n",
" # Remove spaces from the user input\n",
" unicode_input = unicode_input.replace(\" \", \"\")\n",
"\n",
" # Translate Sinhala Unicode to Singlish\n",
" singlish_input = ''.join([sinhala_to_singlish.get(c, c) for c in unicode_input])\n",
"\n",
" # Update the Singlish text display\n",
" singlish_text.delete(1.0, tk.END)\n",
" singlish_text.insert(tk.END, singlish_input)\n",
"\n",
" # Retrieve letter images for the Singlish input\n",
" letter_images = []\n",
" combined_string = \"\"\n",
" for letter in singlish_input:\n",
" combined_string += letter\n",
" letter_image = get_letter_image(combined_string)\n",
" if letter_image is not None:\n",
" letter_image = letter_image.resize(image_size) # Adjust the size if needed\n",
" letter_image = np.array(letter_image) / 255.0 # Normalize the image if needed\n",
" letter_images.append(letter_image)\n",
" combined_string = \"\"\n",
"\n",
" # Combine the letter images into a GIF\n",
" gif_images = []\n",
" for letter_image in letter_images:\n",
" image_pil = Image.fromarray((letter_image * 255).astype(np.uint8))\n",
" gif_images.append(image_pil)\n",
"\n",
" # Save the combined images as a GIF\n",
" output_path = \"C:/Users/himashara/Documents/signlanguage/generated-gif/generated.gif\"\n",
" gif_images[0].save(output_path, save_all=True, append_images=gif_images[1:], loop=0, duration=1200)\n",
"\n",
" # Display the generated GIF\n",
" display_gif(output_path)\n",
"\n",
"def clear_results():\n",
" # Clear the entered Unicode string\n",
" unicode_entry.delete(0, tk.END)\n",
"\n",
" # Clear the Singlish text display\n",
" singlish_text.delete(1.0, tk.END)\n",
"\n",
" # Clear the GIF display\n",
" gif_label.configure(image=None)\n",
" gif_label.image = None\n",
"\n",
" # Cancel the pending after() calls\n",
" window.after_cancel(gif_animation_id)\n",
"\n",
"def display_gif(output_path):\n",
" # Load the GIF and extract frames using ImageSequence module\n",
" gif_image = Image.open(output_path)\n",
" frames = [frame.copy() for frame in ImageSequence.Iterator(gif_image)]\n",
"\n",
" # Create a Tkinter label widget to display the GIF frames\n",
" global gif_label\n",
" gif_label = ttk.Label(window)\n",
" gif_label.grid(row=3, column=1, columnspan=2, pady=10)\n",
"\n",
" # Recursive function to animate the GIF frames\n",
" def animate_frame(frame_index):\n",
" # Display the current frame on the label\n",
" frame = frames[frame_index]\n",
" frame_image = ImageTk.PhotoImage(frame)\n",
" gif_label.configure(image=frame_image)\n",
" gif_label.image = frame_image\n",
"\n",
" # Calculate the index of the next frame\n",
" next_frame_index = (frame_index + 1) % len(frames)\n",
"\n",
" # Call the function again after a certain delay\n",
" global gif_animation_id\n",
" gif_animation_id = window.after(1200, animate_frame, next_frame_index)\n",
"\n",
" # Start the animation by calling the recursive function with the first frame index\n",
" animate_frame(0)\n",
"\n",
"convert_button = ttk.Button(window, text=\"Convert\", command=convert_unicode_to_singlish)\n",
"convert_button.grid(row=1, column=0, pady=10)\n",
"\n",
"clear_button = ttk.Button(window, text=\"Clear Results\", command=clear_results)\n",
"clear_button.grid(row=1, column=1, pady=10)\n",
"\n",
"# Step 3: View area to display Translated Singlish string\n",
"singlish_label = ttk.Label(window, text=\"Translated Singlish string:\")\n",
"singlish_label.grid(row=2, column=0, sticky=tk.W)\n",
"singlish_text = tk.Text(window, height=1, width=30)\n",
"singlish_text.grid(row=2, column=1, columnspan=2, pady=10)\n",
"\n",
"# Dataset path\n",
"dataset_path = \"C:/Users/himashara/Documents/signlanguage/training/\"\n",
"\n",
"# Translation mapping from Sinhala Unicode to Singlish\n",
"sinhala_to_singlish = {\n",
" \"අ\": \"A\",\n",
" \"ආ\": \"Aah\",\n",
" \"ඇ\": \"Aeh\",\n",
" \"ඉ\": \"Ee\",\n",
" \"ඊ\": \"Eeh\",\n",
" \"උ\": \"Uh\",\n",
" \"ඌ\": \"Uhh\",\n",
" \"එ\": \"A\",\n",
" \"ඒ\": \"Ae\",\n",
" \"ඔ\": \"O\",\n",
" \"ඕ\": \"Ohh\",\n",
" \"ක්\": \"K\",\n",
" \"ග්\": \"Ig\",\n",
" \"ටී\": \"Tee\",\n",
" \"ට\": \"T\"\n",
"}\n",
"\n",
"# Image size\n",
"image_size = (480, 480)\n",
"\n",
"# Function to retrieve the letter image\n",
"def get_letter_image(letter):\n",
" if os.path.exists(os.path.join(dataset_path, letter)):\n",
" letter_path = os.path.join(dataset_path, letter)\n",
" image_files = os.listdir(letter_path)\n",
" image_path = os.path.join(letter_path, image_files[0]) # Assuming only one image per letter\n",
" return Image.open(image_path)\n",
" return None\n",
"\n",
"# Load the pre-trained model\n",
"model = load_model(\"letter_classification_model.h5\")\n",
"\n",
"# Sinhala tokenizer\n",
"tokenizer = SinhalaTokenizer()\n",
"\n",
"# Function to preprocess Sinhala text\n",
"def preprocess_sinhala_text(text):\n",
" tokens = tokenizer.tokenize(text)\n",
" return ' '.join(tokens)\n",
"\n",
"# Start the Tkinter event loop\n",
"window.mainloop()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b2efcfc9",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"id": "0e25256c",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"id": "1028b834",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.13"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
import Curriculum from '../models/curriculum.model.js';
import Tutorial from '../models/tutorial.model.js';
export const getAllCurriculums = async (req, res) => {
try {
......@@ -26,17 +27,41 @@ export const createCurriculum = async (req, res) => {
const curriculumData = req.body;
try {
const newCurriculum = new Curriculum(curriculumData);
// Calculate total tutorial mark
let totalTutorialMark = 0;
for (const tutorialId of curriculumData.tutorials) {
const tutorial = await Tutorial.findById(tutorialId);
const tutorialMarks = typeof tutorial.tutorialMarks === 'string' ? parseFloat(tutorial.tutorialMarks) : tutorial.tutorialMarks;
totalTutorialMark += isNaN(tutorialMarks) ? 0 : tutorialMarks;
}
newCurriculum.curriculumMark = totalTutorialMark;
await newCurriculum.save();
res.status(201).json(newCurriculum);
} catch (error) {
res.status(400).json({ message: error.message });
}
}
export const updateCurriculum = async (req, res) => {
const { id } = req.params;
const updatedCurriculum = req.body;
try {
const curriculum = await Curriculum.findById(id);
// Calculate total tutorial mark
let totalTutorialMark = 0;
for (const tutorialId of updatedCurriculum.tutorials) {
const tutorial = await Tutorial.findById(tutorialId);
const tutorialMarks = typeof tutorial.tutorialMarks === 'string' ? parseFloat(tutorial.tutorialMarks) : tutorial.tutorialMarks;
totalTutorialMark += isNaN(tutorialMarks) ? 0 : tutorialMarks;
}
updatedCurriculum.curriculumMark = totalTutorialMark;
const result = await Curriculum.findByIdAndUpdate(id, updatedCurriculum, { new: true });
res.status(200).json(result);
} catch (error) {
......@@ -48,7 +73,7 @@ export const deleteCurriculum = async (req, res) => {
const { id } = req.params;
try {
await Curriculum.findByIdAndDelete(id);
res.status(200).json({ message: 'Curriculum deleted successfully' });
res.status(200).json({ _id: id, message: 'Curriculum deleted successfully' });
} catch (error) {
res.status(404).json({ message: 'Curriculum not found' });
}
......
import { exec } from "child_process";
export const marksCalculator = async (req, res) => {
const imageData = req.file.buffer.toString('base64');
const targetClass = req.body.class;
const { curriculumIndex, tutorialIndex } = req.params;
try {
// console.log(req.file);
const status = "";
// if (!req.file || !req.body.class || !req.params.curriculumIndex || !req.params.tutorialIndex) {
// return res.status(400).json({ code: "02", message: "Missing required data" });
// }
// console.log(curriculumIndex, tutorialIndex);
try {
const imageData = req.file.buffer.toString('base64');
const targetClass = req.body.class;
const { curriculumIndex, tutorialIndex } = req.params;
if (curriculumIndex == 1 && tutorialIndex == 1) {
// Run Python script to perform prediction
const pythonProcess = exec('python prediction_config/C1T1/predict.py', (error, stdout, stderr) => {
if (error) {
console.error(error);
return res.status(500).json({ error: 'An error occurred' });
return res.status(500).json({ code: '03', message: 'An error occurred while running the prediction script' });
}
const [predicted_class_name, confidence] = stdout.trim().split(',');
const parsedConfidence = parseFloat(confidence).toFixed(2);
let status = "";
if (predicted_class_name === targetClass && parsedConfidence > 85) {
......@@ -42,9 +45,10 @@ export const marksCalculator = async (req, res) => {
pythonProcess.stdin.write(`${imageData}\n${targetClass}`);
pythonProcess.stdin.end();
} else {
return res.status(400).json({ code: "02", message: "Curriculum Index or Tutorial Index Invalid" })
return res.status(400).json({ code: "02", message: "Curriculum Index or Tutorial Index Invalid" });
}
} catch (error) {
res.status(500).json({ code: "00", message: "Something went wrong" })
console.error(error);
res.status(500).json({ code: "00", message: "Something went wrong" });
}
}
\ No newline at end of file
}
......@@ -21,32 +21,64 @@ export const getTutorialById = async (req, res) => {
export const createTutorial = async (req, res) => {
const tutorialData = req.body;
// Calculate total tutorial marks based on task item marks
const totalTaskMarks = tutorialData.taskItems.reduce((total, task) => {
const taskItemMark = typeof task.taskItemMark === 'string' ? parseFloat(task.taskItemMark) : task.taskItemMark;
return total + (isNaN(taskItemMark) ? 0 : taskItemMark);
}, 0);
tutorialData.tutorialMarks = totalTaskMarks;
try {
const newTutorial = new Tutorial(tutorialData);
await newTutorial.save();
res.status(201).json(newTutorial);
} catch (error) {
res.status(400).json({ message: error.message });
res.status(500).json({ message: 'Failed to create tutorial', error: error.message });
}
}
};
export const updateTutorial = async (req, res) => {
const { id } = req.params;
const updatedTutorial = req.body;
const updatedTutorialData = req.body;
// Calculate total tutorial marks based on updated task item marks
const totalTaskMarks = updatedTutorialData.taskItems.reduce((total, task) => {
const taskItemMark = typeof task.taskItemMark === 'string' ? parseFloat(task.taskItemMark) : task.taskItemMark;
return total + (isNaN(taskItemMark) ? 0 : taskItemMark);
}, 0);
updatedTutorialData.tutorialMarks = totalTaskMarks;
try {
const result = await Tutorial.findByIdAndUpdate(id, updatedTutorial, { new: true });
res.status(200).json(result);
const tutorial = await Tutorial.findById(id);
if (!tutorial) {
return res.status(404).json({ message: 'Tutorial not found' });
}
// Update the tutorial with the new data
const updatedTutorial = await Tutorial.findByIdAndUpdate(id, updatedTutorialData, { new: true });
res.status(200).json(updatedTutorial);
} catch (error) {
res.status(404).json({ message: 'Tutorial not found' });
res.status(500).json({ message: error.message });
}
}
};
export const deleteTutorial = async (req, res) => {
const { id } = req.params;
try {
const tutorial = await Tutorial.findById(id);
if (!tutorial) {
return res.status(404).json({ message: 'Tutorial not found' });
}
await Tutorial.findByIdAndDelete(id);
res.status(200).json({ message: 'Tutorial deleted successfully' });
res.status(200).json({ _id: id, message: 'Tutorial deleted successfully' });
} catch (error) {
res.status(404).json({ message: 'Tutorial not found' });
res.status(500).json({ message: error.message });
}
}
};
......@@ -157,3 +157,23 @@ export const deleteUser = async (req, res) => {
}
}
export const currentUser = async (req, res) => {
try {
// req.userId contains the user ID extracted from the token by the auth middleware
const userId = req.userId;
// Fetch the user account information from the database
const user = await User.findById(userId);
if (!user) {
return res.status(404).json({ message: 'User not found' });
}
// Send the user account information as a response
res.status(200).json({ user });
} catch (error) {
console.error(error);
res.status(500).json({ message: 'Server error' });
}
}
import UserProgress from '../models/userProgress.model.js';
import Curriculum from "../models/curriculum.model.js";
import UserProgress from "../models/userProgress.model.js";
export const subscribeCurriculum = async (req, res) => {
const { userId, curriculum } = req.body;
try {
// Check if the user is already subscribed to the curriculum
let userProgress = await UserProgress.findOne({ userId });
if (!userProgress) {
// If there is no user progress record for this user, create a new one
userProgress = new UserProgress({
userId,
curriculums: [curriculum],
});
} else {
// If there is an existing user progress record, check if the curriculum already exists
const existingCurriculumIndex = userProgress.curriculums.findIndex(c => c.curriculumCode === curriculum.curriculumCode);
if (existingCurriculumIndex !== -1) {
// If the curriculum exists, update it
return res.status(400).json({ error: 'User already subscribed to this curriculum' });
} else {
// If the curriculum doesn't exist, add it to the array
userProgress.curriculums.push(curriculum);
}
}
// Update the totalCurriculumsMarks based on curriculum marks
const totalCurriculumsMarks = userProgress.curriculums.reduce((total, curriculum) => {
const curriculumMark = typeof curriculum.curriculumMark === 'string' ? parseFloat(curriculum.curriculumMark) : curriculum.curriculumMark;
return total + (isNaN(curriculumMark) ? 0 : curriculumMark);
}, 0);
userProgress.totalCurriculumsMarks = totalCurriculumsMarks;
const curriculumCode = curriculum.curriculumCode
// Now, update the curriculum collection with the subscribed user
await Curriculum.updateOne(
{ curriculumCode },
{ $addToSet: { subscribedUser: userId } }
);
// Save the user progress record
await userProgress.save();
res.status(200).json({ message: 'Curriculum subscription successful' });
} catch (error) {
console.error(error);
res.status(500).json({ error: 'Internal server error' });
}
};
// Get user's curriculum subscriptions and progress
export const getUserProgress = async (req, res) => {
const userId = req.params.userId;
try {
const userProgress = await UserProgress.findOne({ userId }).populate('curriculumId tutorialProgress.tutorialId');
res.status(200).json(userProgress);
const userProgress = await UserProgress.findOne({ userId });
if (!userProgress) {
return res.status(404).json({ message: 'User progress not found' });
}
res.status(200).json({ userProgress });
} catch (error) {
res.status(500).json({ message: error.message });
console.error(error);
res.status(500).json({ error: 'Internal server error' });
}
}
};
export const updateUserProgress = async (req, res) => {
const userId = req.params.userId;
const { curriculumId, tutorialId, completed, marks } = req.body;
// Update task item progress and calculate overall progress
export const updateTaskItemProgress = async (req, res) => {
const { userId, curriculumCode, tutorialCode, taskItemTitle, taskItemMarkUser, taskItemSpentTime } = req.body;
try {
let userProgress = await UserProgress.findOne({ userId });
const userProgress = await UserProgress.findOne({ userId });
if (!userProgress) {
userProgress = new UserProgress({ userId });
return res.status(404).json({ message: 'User progress not found' });
}
const curriculumProgress = userProgress.curriculumId.find(prog => prog.curriculumId.equals(curriculumId));
// Find the curriculum, tutorial, and task item to update
const curriculumIndex = userProgress.curriculums.findIndex(c => c.curriculumCode === curriculumCode);
if (curriculumIndex === -1) {
return res.status(404).json({ message: 'Curriculum not found' });
}
if (!curriculumProgress) {
userProgress.curriculumId.push({ curriculumId });
const tutorialIndex = userProgress.curriculums[curriculumIndex].tutorials.findIndex(t => t.tutorialCode === tutorialCode);
if (tutorialIndex === -1) {
return res.status(404).json({ message: 'Tutorial not found' });
}
const tutorialProgress = curriculumProgress.tutorialProgress.find(prog => prog.tutorialId.equals(tutorialId));
if (!tutorialProgress) {
curriculumProgress.tutorialProgress.push({ tutorialId, completed });
} else {
tutorialProgress.completed = completed;
const taskItemIndex = userProgress.curriculums[curriculumIndex].tutorials[tutorialIndex].taskItems.findIndex(item => item.title === taskItemTitle);
if (taskItemIndex === -1) {
return res.status(404).json({ message: 'Task item not found' });
}
userProgress.marks = marks;
// Update task item progress
const taskItem = userProgress.curriculums[curriculumIndex].tutorials[tutorialIndex].taskItems[taskItemIndex];
taskItem.taskItemMarkUser = typeof taskItemMarkUser === 'string' ? parseFloat(taskItemMarkUser) : taskItemMarkUser;
taskItem.taskItemSpentTime = taskItemSpentTime;
// Calculate total task marks and spent time for the tutorial
const tutorial = userProgress.curriculums[curriculumIndex].tutorials[tutorialIndex];
tutorial.tutorialMarkUser = tutorial.taskItems.reduce((total, item) => total + item.taskItemMarkUser, 0);
tutorial.tutorialSpentTime = tutorial.taskItems.reduce((total, item) => total + item.taskItemSpentTime, 0);
// Calculate total tutorial marks and spent time for the curriculum
const curriculum = userProgress.curriculums[curriculumIndex];
curriculum.curriculumMarkUser = curriculum.tutorials.reduce((total, t) => total + t.tutorialMarkUser, 0);
curriculum.curriculumSpentTime = curriculum.tutorials.reduce((total, t) => total + t.tutorialSpentTime, 0);
// Calculate total curriculum marks and spent time for the user
userProgress.totalCurriculumsMarks = userProgress.curriculums.reduce((total, c) => total + c.curriculumMarkUser, 0);
userProgress.totalCurriculumSpentTime = userProgress.curriculums.reduce((total, c) => total + c.curriculumSpentTime, 0);
// Save the updated user progress
await userProgress.save();
res.status(200).json(userProgress);
res.status(200).json({ message: 'Task item progress updated successfully' });
} catch (error) {
res.status(500).json({ message: error.message });
console.error(error);
res.status(500).json({ error: 'Internal server error' });
}
}
};
......@@ -11,17 +11,21 @@ const commonFields = {
type: Date,
default: new Date(),
},
};
const curriculumSchema = new mongoose.Schema({
};
const curriculumSchema = new mongoose.Schema({
curriculumCode: {
type: String,
unique: true, // Ensures unique values for curriculumCode
},
curriculumLevel: String,
curriculumLevel: Number,
curriculumTitle: String,
curriculumDescription: String,
curriculumImage: String,
curriculumMark: {
type: Number,
default: 0
},
tutorials: [{
type: mongoose.Schema.Types.ObjectId,
ref: "Tutorial",
......@@ -31,7 +35,11 @@ const curriculumSchema = new mongoose.Schema({
type: Number,
default: 1, // Default status as active (1)
},
...commonFields
...commonFields,
subscribedUser: [{
type: mongoose.Schema.Types.ObjectId,
ref: "User", // Reference to the User model if you have one
}],
});
const Curriculum = mongoose.model("Curriculum", curriculumSchema);
......
......@@ -16,9 +16,13 @@ const commonFields = {
const taskItemSchema = new mongoose.Schema({
title: String,
description: String,
howToDo: String,
howToDo: [String],
referenceImage: String,
referenceVideo: String,
taskItemMark : {
type: Number,
default: 0
}
// Additional fields for task items
});
......@@ -30,6 +34,10 @@ const tutorialSchema = new mongoose.Schema({
tutorialTitle: String,
tutorialDescription: String,
tutorialImage: String,
tutorialMarks: {
type: Number,
default: 0
},
taskItems: [taskItemSchema], // Embed task items as subdocuments
// Additional fields for tutorial details
status: {
......
import mongoose from "mongoose";
const taskItemSchema = new mongoose.Schema({
title: {
type: String,
required: true,
},
description: {
type: String,
required: true,
},
howToDo: [String],
referenceImage: String,
referenceVideo: String,
taskItemMark: {
type: Number,
default: 0
},
taskItemMarkUser: {
type: Number,
default: 0
},
taskItemSpentTime: {
type: Number,
default: 0
},
}, { _id: false });
const tutorialTypeUserProgressSchema = new mongoose.Schema({
tutorialCode: String,
tutorialTitle: String,
tutorialDescription: String,
tutorialImage: String,
tutorialMarks: {
type: Number,
default: 0
},
tutorialMarkUser: {
type: Number,
default: 0
},
tutorialSpentTime: {
type: Number,
default: 0
},
taskItems: [taskItemSchema],
}, { _id: false });
const curriculumTypeUserProgressSchema = new mongoose.Schema({
curriculumCode: String,
curriculumLevel: Number,
curriculumTitle: String,
curriculumDescription: String,
curriculumImage: String,
curriculumMark: {
type: Number,
default: 0
},
curriculumMarkUser: {
type: Number,
default: 0
},
curriculumSpentTime: {
type: Number,
default: 0
},
tutorials: [tutorialTypeUserProgressSchema],
}, { _id: false });
const userProgressSchema = new mongoose.Schema({
userId: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'User' // Reference to the User model
ref: 'User'
},
curriculums: [curriculumTypeUserProgressSchema],
totalCurriculumsMarks: {
type: Number,
default: 0
},
totalCurriculumSpentTime: {
type: Number,
default: 0
},
status: {
type: Number,
default: 1,
},
createdBy: String,
createdAt: {
type: Date,
default: new Date(),
},
updatedBy: String,
updatedAt: {
type: Date,
default: new Date(),
},
curriculumId: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'Curriculum' // Reference to the Curriculum model
},
tutorialProgress: [
{
tutorialId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Tutorial' // Reference to the Tutorial model
},
completed: {
type: Boolean,
default: false
}
}
],
marks: {
type: Number,
default: 0
}
});
const UserProgress = mongoose.model("UserProgress", userProgressSchema);
......
import express from "express";
import { deleteUser, getUser, getUserAccordingToType, getUsers, signIn, signUp, updateUser } from "../controllers/user.controller.js";
import { currentUser, deleteUser, getUser, getUserAccordingToType, getUsers, signIn, signUp, updateUser } from "../controllers/user.controller.js";
import auth from "../middleware/auth.middleware.js";
const router = express.Router();
router.post('/sign-in', signIn)
router.post('/sign-up', signUp)
router.get('/all', getUsers);
router.get('/current-user', auth, currentUser);
router.get('/:id', getUser);
router.get('/all/type/:userType', getUserAccordingToType);
router.put('/:id', updateUser);
......
import express from "express";
import { getUserProgress, updateUserProgress } from "../controllers/userProgress.controller.js";
import { getUserProgress, subscribeCurriculum, updateTaskItemProgress } from "../controllers/userProgress.controller.js";
const router = express.Router();
router.post('/subscribe-curriculum', subscribeCurriculum);
router.get('/:userId', getUserProgress);
router.post('/:userId', updateUserProgress);
router.put('/update-task-item-progress', updateTaskItemProgress);
export default router;
......@@ -23,9 +23,13 @@ import userProgressRoutes from "./routes/userProgress.routes.js";
dotenv.config();
const app = express();
const corsOptions = {
origin: 'http://localhost:3000',
};
app.use(bodyParser.json({ limit: "30mb", extended: true }));
app.use(bodyParser.urlencoded({ limit: "30mb", extended: true }));
app.use(cors());
app.use(cors(corsOptions));
//end
app.get("/", (req, res) => {
......
......@@ -2,1062 +2,1100 @@
# yarn lockfile v1
"@mongodb-js/saslprep@^1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@mongodb-js/saslprep/-/saslprep-1.1.0.tgz#022fa36620a7287d17acd05c4aae1e5f390d250d"
integrity sha512-Xfijy7HvfzzqiOAhAepF4SGN5e9leLkMvg/OPOF97XemjfVCYN/oWa75wnkc6mltMSTwY+XlbhWgUOJmkFspSw==
dependencies:
sparse-bitfield "^3.0.3"
"@types/node@*":
version "20.5.7"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.5.7.tgz#4b8ecac87fbefbc92f431d09c30e176fc0a7c377"
integrity sha512-dP7f3LdZIysZnmvP3ANJYTSwg+wLLl8p7RqniVlV7j+oXSXAbt9h0WIBFmJy5inWZoX9wZN6eXx+YXd9Rh3RBA==
"integrity" "sha512-CTO/wa8x+rZU626cL2BlbCDzydgnFNgc19h4YvizpTO88MFQxab8wqisxaofQJ/9bLGugRdWIuX/TbIs6VVF6g=="
"resolved" "https://registry.npmjs.org/@types/node/-/node-20.1.2.tgz"
"version" "20.1.2"
"@types/webidl-conversions@*":
version "7.0.0"
resolved "https://registry.yarnpkg.com/@types/webidl-conversions/-/webidl-conversions-7.0.0.tgz#2b8e60e33906459219aa587e9d1a612ae994cfe7"
integrity sha512-xTE1E+YF4aWPJJeUzaZI5DRntlkY3+BCVJi0axFptnjGmAoWxkyREIh/XMrfxVLejwQxMCfDXdICo0VLxThrog==
"integrity" "sha512-xTE1E+YF4aWPJJeUzaZI5DRntlkY3+BCVJi0axFptnjGmAoWxkyREIh/XMrfxVLejwQxMCfDXdICo0VLxThrog=="
"resolved" "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.0.tgz"
"version" "7.0.0"
"@types/whatwg-url@^8.2.1":
version "8.2.2"
resolved "https://registry.yarnpkg.com/@types/whatwg-url/-/whatwg-url-8.2.2.tgz#749d5b3873e845897ada99be4448041d4cc39e63"
integrity sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==
"integrity" "sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA=="
"resolved" "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz"
"version" "8.2.2"
dependencies:
"@types/node" "*"
"@types/webidl-conversions" "*"
abbrev@1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
accepts@~1.3.8:
version "1.3.8"
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e"
integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==
dependencies:
mime-types "~2.1.34"
negotiator "0.6.3"
ansi-regex@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
integrity sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==
ansi-styles@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
integrity sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==
anymatch@~3.1.2:
version "3.1.3"
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e"
integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==
dependencies:
normalize-path "^3.0.0"
picomatch "^2.0.4"
append-field@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/append-field/-/append-field-1.0.0.tgz#1e3440e915f0b1203d23748e78edd7b9b5b43e56"
integrity sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==
array-flatten@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==
balanced-match@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
bcryptjs@^2.4.3:
version "2.4.3"
resolved "https://registry.yarnpkg.com/bcryptjs/-/bcryptjs-2.4.3.tgz#9ab5627b93e60621ff7cdac5da9733027df1d0cb"
integrity sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==
binary-extensions@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
body-parser@1.20.1:
version "1.20.1"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668"
integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==
dependencies:
bytes "3.1.2"
content-type "~1.0.4"
debug "2.6.9"
depd "2.0.0"
destroy "1.2.0"
http-errors "2.0.0"
iconv-lite "0.4.24"
on-finished "2.4.1"
qs "6.11.0"
raw-body "2.5.1"
type-is "~1.6.18"
unpipe "1.0.0"
body-parser@^1.20.2:
version "1.20.2"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd"
integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==
dependencies:
bytes "3.1.2"
content-type "~1.0.5"
debug "2.6.9"
depd "2.0.0"
destroy "1.2.0"
http-errors "2.0.0"
iconv-lite "0.4.24"
on-finished "2.4.1"
qs "6.11.0"
raw-body "2.5.2"
type-is "~1.6.18"
unpipe "1.0.0"
brace-expansion@^1.1.7:
version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
dependencies:
balanced-match "^1.0.0"
concat-map "0.0.1"
braces@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
dependencies:
fill-range "^7.0.1"
bson@^5.4.0:
version "5.4.0"
resolved "https://registry.yarnpkg.com/bson/-/bson-5.4.0.tgz#0eea77276d490953ad8616b483298dbff07384c6"
integrity sha512-WRZ5SQI5GfUuKnPTNmAYPiKIof3ORXAF4IRU5UcgmivNIon01rWQlw5RUH954dpu8yGL8T59YShVddIPaU/gFA==
buffer-equal-constant-time@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==
buffer-from@^1.0.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
busboy@^1.0.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893"
integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==
dependencies:
streamsearch "^1.1.0"
bytes@3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5"
integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==
call-bind@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c"
integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==
dependencies:
function-bind "^1.1.1"
get-intrinsic "^1.0.2"
chalk@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
integrity sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==
dependencies:
ansi-styles "^2.2.1"
escape-string-regexp "^1.0.2"
has-ansi "^2.0.0"
strip-ansi "^3.0.0"
supports-color "^2.0.0"
chokidar@^3.5.2:
version "3.5.3"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
dependencies:
anymatch "~3.1.2"
braces "~3.0.2"
glob-parent "~5.1.2"
is-binary-path "~2.1.0"
is-glob "~4.0.1"
normalize-path "~3.0.0"
readdirp "~3.6.0"
"abbrev@1":
"integrity" "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
"resolved" "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz"
"version" "1.1.1"
"accepts@~1.3.8":
"integrity" "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw=="
"resolved" "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz"
"version" "1.3.8"
dependencies:
"mime-types" "~2.1.34"
"negotiator" "0.6.3"
"ansi-regex@^2.0.0":
"integrity" "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA=="
"resolved" "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz"
"version" "2.1.1"
"ansi-styles@^2.2.1":
"integrity" "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA=="
"resolved" "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz"
"version" "2.2.1"
"anymatch@~3.1.2":
"integrity" "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="
"resolved" "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz"
"version" "3.1.3"
dependencies:
"normalize-path" "^3.0.0"
"picomatch" "^2.0.4"
"append-field@^1.0.0":
"integrity" "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw=="
"resolved" "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz"
"version" "1.0.0"
"array-flatten@1.1.1":
"integrity" "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
"resolved" "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz"
"version" "1.1.1"
"balanced-match@^1.0.0":
"integrity" "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
"resolved" "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz"
"version" "1.0.2"
"bcryptjs@^2.4.3":
"integrity" "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ=="
"resolved" "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz"
"version" "2.4.3"
"binary-extensions@^2.0.0":
"integrity" "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA=="
"resolved" "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz"
"version" "2.2.0"
"body-parser@^1.20.2":
"integrity" "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA=="
"resolved" "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz"
"version" "1.20.2"
dependencies:
"bytes" "3.1.2"
"content-type" "~1.0.5"
"debug" "2.6.9"
"depd" "2.0.0"
"destroy" "1.2.0"
"http-errors" "2.0.0"
"iconv-lite" "0.4.24"
"on-finished" "2.4.1"
"qs" "6.11.0"
"raw-body" "2.5.2"
"type-is" "~1.6.18"
"unpipe" "1.0.0"
"body-parser@1.20.1":
"integrity" "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw=="
"resolved" "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz"
"version" "1.20.1"
dependencies:
"bytes" "3.1.2"
"content-type" "~1.0.4"
"debug" "2.6.9"
"depd" "2.0.0"
"destroy" "1.2.0"
"http-errors" "2.0.0"
"iconv-lite" "0.4.24"
"on-finished" "2.4.1"
"qs" "6.11.0"
"raw-body" "2.5.1"
"type-is" "~1.6.18"
"unpipe" "1.0.0"
"brace-expansion@^1.1.7":
"integrity" "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="
"resolved" "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz"
"version" "1.1.11"
dependencies:
"balanced-match" "^1.0.0"
"concat-map" "0.0.1"
"braces@~3.0.2":
"integrity" "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A=="
"resolved" "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz"
"version" "3.0.2"
dependencies:
"fill-range" "^7.0.1"
"bson@^5.2.0":
"integrity" "sha512-ukmCZMneMlaC5ebPHXIkP8YJzNl5DC41N5MAIvKDqLggdao342t4McltoJBQfQya/nHBWAcSsYRqlXPoQkTJag=="
"resolved" "https://registry.npmjs.org/bson/-/bson-5.3.0.tgz"
"version" "5.3.0"
"buffer-equal-constant-time@1.0.1":
"integrity" "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="
"resolved" "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz"
"version" "1.0.1"
"buffer-from@^1.0.0":
"integrity" "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
"resolved" "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz"
"version" "1.1.2"
"busboy@^1.0.0":
"integrity" "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA=="
"resolved" "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz"
"version" "1.6.0"
dependencies:
"streamsearch" "^1.1.0"
"bytes@3.1.2":
"integrity" "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="
"resolved" "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz"
"version" "3.1.2"
"call-bind@^1.0.0":
"integrity" "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA=="
"resolved" "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz"
"version" "1.0.2"
dependencies:
"function-bind" "^1.1.1"
"get-intrinsic" "^1.0.2"
"chalk@^1.1.3":
"integrity" "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A=="
"resolved" "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz"
"version" "1.1.3"
dependencies:
"ansi-styles" "^2.2.1"
"escape-string-regexp" "^1.0.2"
"has-ansi" "^2.0.0"
"strip-ansi" "^3.0.0"
"supports-color" "^2.0.0"
"chokidar@^3.5.2":
"integrity" "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw=="
"resolved" "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz"
"version" "3.5.3"
dependencies:
"anymatch" "~3.1.2"
"braces" "~3.0.2"
"glob-parent" "~5.1.2"
"is-binary-path" "~2.1.0"
"is-glob" "~4.0.1"
"normalize-path" "~3.0.0"
"readdirp" "~3.6.0"
optionalDependencies:
fsevents "~2.3.2"
concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
concat-stream@^1.5.2:
version "1.6.2"
resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34"
integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==
dependencies:
buffer-from "^1.0.0"
inherits "^2.0.3"
readable-stream "^2.2.2"
typedarray "^0.0.6"
content-disposition@0.5.4:
version "0.5.4"
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe"
integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==
dependencies:
safe-buffer "5.2.1"
content-type@~1.0.4, content-type@~1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918"
integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==
cookie-signature@1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==
cookie@0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b"
integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==
core-util-is@~1.0.0:
version "1.0.3"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85"
integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==
cors@^2.8.5:
version "2.8.5"
resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29"
integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==
dependencies:
object-assign "^4"
vary "^1"
debug@2.6.9:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
dependencies:
ms "2.0.0"
debug@4.x:
version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
dependencies:
ms "2.1.2"
debug@^3.2.7:
version "3.2.7"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==
dependencies:
ms "^2.1.1"
depd@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
destroy@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015"
integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==
dotenv@^16.0.3:
version "16.3.1"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e"
integrity sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==
ecdsa-sig-formatter@1.0.11:
version "1.0.11"
resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf"
integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==
dependencies:
safe-buffer "^5.0.1"
ee-first@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
encodeurl@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==
escape-html@~1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==
escape-string-regexp@^1.0.2:
version "1.0.5"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==
etag@~1.8.1:
version "1.8.1"
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==
express@^4.18.2:
version "4.18.2"
resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59"
integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==
dependencies:
accepts "~1.3.8"
array-flatten "1.1.1"
body-parser "1.20.1"
content-disposition "0.5.4"
content-type "~1.0.4"
cookie "0.5.0"
cookie-signature "1.0.6"
debug "2.6.9"
depd "2.0.0"
encodeurl "~1.0.2"
escape-html "~1.0.3"
etag "~1.8.1"
finalhandler "1.2.0"
fresh "0.5.2"
http-errors "2.0.0"
merge-descriptors "1.0.1"
methods "~1.1.2"
on-finished "2.4.1"
parseurl "~1.3.3"
path-to-regexp "0.1.7"
proxy-addr "~2.0.7"
qs "6.11.0"
range-parser "~1.2.1"
safe-buffer "5.2.1"
send "0.18.0"
serve-static "1.15.0"
setprototypeof "1.2.0"
statuses "2.0.1"
type-is "~1.6.18"
utils-merge "1.0.1"
vary "~1.1.2"
fill-range@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
dependencies:
to-regex-range "^5.0.1"
finalhandler@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32"
integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==
dependencies:
debug "2.6.9"
encodeurl "~1.0.2"
escape-html "~1.0.3"
on-finished "2.4.1"
parseurl "~1.3.3"
statuses "2.0.1"
unpipe "~1.0.0"
forwarded@0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==
fresh@0.5.2:
version "0.5.2"
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==
fsevents@~2.3.2:
version "2.3.3"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
function-bind@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
get-intrinsic@^1.0.2:
version "1.2.1"
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.1.tgz#d295644fed4505fc9cde952c37ee12b477a83d82"
integrity sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==
dependencies:
function-bind "^1.1.1"
has "^1.0.3"
has-proto "^1.0.1"
has-symbols "^1.0.3"
glob-parent@~5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
dependencies:
is-glob "^4.0.1"
has-ansi@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
integrity sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==
dependencies:
ansi-regex "^2.0.0"
has-flag@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==
has-proto@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0"
integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==
has-symbols@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8"
integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==
has@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
dependencies:
function-bind "^1.1.1"
http-errors@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3"
integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==
dependencies:
depd "2.0.0"
inherits "2.0.4"
setprototypeof "1.2.0"
statuses "2.0.1"
toidentifier "1.0.1"
iconv-lite@0.4.24:
version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
dependencies:
safer-buffer ">= 2.1.2 < 3"
ignore-by-default@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09"
integrity sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==
inherits@2.0.4, inherits@^2.0.3, inherits@~2.0.3:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
ip@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da"
integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==
ipaddr.js@1.9.1:
version "1.9.1"
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3"
integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==
is-binary-path@~2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
dependencies:
binary-extensions "^2.0.0"
is-extglob@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
is-glob@^4.0.1, is-glob@~4.0.1:
version "4.0.3"
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
dependencies:
is-extglob "^2.1.1"
is-number@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==
jsonwebtoken@^9.0.0:
version "9.0.1"
resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-9.0.1.tgz#81d8c901c112c24e497a55daf6b2be1225b40145"
integrity sha512-K8wx7eJ5TPvEjuiVSkv167EVboBDv9PZdDoF7BgeQnBLVvZWW9clr2PsQHVJDTKaEIH5JBIwHujGcHp7GgI2eg==
dependencies:
jws "^3.2.2"
lodash "^4.17.21"
ms "^2.1.1"
semver "^7.3.8"
jwa@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a"
integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==
dependencies:
buffer-equal-constant-time "1.0.1"
ecdsa-sig-formatter "1.0.11"
safe-buffer "^5.0.1"
jws@^3.2.2:
version "3.2.2"
resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304"
integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==
dependencies:
jwa "^1.4.1"
safe-buffer "^5.0.1"
kareem@2.5.1:
version "2.5.1"
resolved "https://registry.yarnpkg.com/kareem/-/kareem-2.5.1.tgz#7b8203e11819a8e77a34b3517d3ead206764d15d"
integrity sha512-7jFxRVm+jD+rkq3kY0iZDJfsO2/t4BBPeEb2qKn2lR/9KhuksYk5hxzfRYWMPV8P/x2d0kHD306YyWLzjjH+uA==
lodash@^4.17.21:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
lru-cache@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
dependencies:
yallist "^4.0.0"
media-typer@0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==
memory-pager@^1.0.2:
version "1.5.0"
resolved "https://registry.yarnpkg.com/memory-pager/-/memory-pager-1.5.0.tgz#d8751655d22d384682741c972f2c3d6dfa3e66b5"
integrity sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==
merge-descriptors@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==
methods@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==
mime-db@1.52.0:
version "1.52.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
mime-types@~2.1.24, mime-types@~2.1.34:
version "2.1.35"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
dependencies:
mime-db "1.52.0"
mime@1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
minimatch@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
dependencies:
brace-expansion "^1.1.7"
minimist@^1.2.6:
version "1.2.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
mkdirp@^0.5.4:
version "0.5.6"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6"
integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==
dependencies:
minimist "^1.2.6"
mongodb-connection-string-url@^2.6.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz#57901bf352372abdde812c81be47b75c6b2ec5cf"
integrity sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==
"fsevents" "~2.3.2"
"concat-map@0.0.1":
"integrity" "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
"resolved" "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz"
"version" "0.0.1"
"concat-stream@^1.5.2":
"integrity" "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw=="
"resolved" "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz"
"version" "1.6.2"
dependencies:
"buffer-from" "^1.0.0"
"inherits" "^2.0.3"
"readable-stream" "^2.2.2"
"typedarray" "^0.0.6"
"content-disposition@0.5.4":
"integrity" "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ=="
"resolved" "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz"
"version" "0.5.4"
dependencies:
"safe-buffer" "5.2.1"
"content-type@~1.0.4", "content-type@~1.0.5":
"integrity" "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="
"resolved" "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz"
"version" "1.0.5"
"cookie-signature@1.0.6":
"integrity" "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
"resolved" "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz"
"version" "1.0.6"
"cookie@0.5.0":
"integrity" "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw=="
"resolved" "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz"
"version" "0.5.0"
"core-util-is@~1.0.0":
"integrity" "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
"resolved" "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz"
"version" "1.0.3"
"cors@^2.8.5":
"integrity" "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g=="
"resolved" "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz"
"version" "2.8.5"
dependencies:
"object-assign" "^4"
"vary" "^1"
"debug@^3.2.7":
"integrity" "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="
"resolved" "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz"
"version" "3.2.7"
dependencies:
"ms" "^2.1.1"
"debug@2.6.9":
"integrity" "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="
"resolved" "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz"
"version" "2.6.9"
dependencies:
"ms" "2.0.0"
"debug@4.x":
"integrity" "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ=="
"resolved" "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz"
"version" "4.3.4"
dependencies:
"ms" "2.1.2"
"depd@2.0.0":
"integrity" "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
"resolved" "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz"
"version" "2.0.0"
"destroy@1.2.0":
"integrity" "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="
"resolved" "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz"
"version" "1.2.0"
"dotenv@^16.0.3":
"integrity" "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ=="
"resolved" "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz"
"version" "16.0.3"
"ecdsa-sig-formatter@1.0.11":
"integrity" "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="
"resolved" "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz"
"version" "1.0.11"
dependencies:
"safe-buffer" "^5.0.1"
"ee-first@1.1.1":
"integrity" "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
"resolved" "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz"
"version" "1.1.1"
"encodeurl@~1.0.2":
"integrity" "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="
"resolved" "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz"
"version" "1.0.2"
"escape-html@~1.0.3":
"integrity" "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
"resolved" "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz"
"version" "1.0.3"
"escape-string-regexp@^1.0.2":
"integrity" "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="
"resolved" "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz"
"version" "1.0.5"
"etag@~1.8.1":
"integrity" "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="
"resolved" "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz"
"version" "1.8.1"
"express@^4.18.2":
"integrity" "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ=="
"resolved" "https://registry.npmjs.org/express/-/express-4.18.2.tgz"
"version" "4.18.2"
dependencies:
"accepts" "~1.3.8"
"array-flatten" "1.1.1"
"body-parser" "1.20.1"
"content-disposition" "0.5.4"
"content-type" "~1.0.4"
"cookie" "0.5.0"
"cookie-signature" "1.0.6"
"debug" "2.6.9"
"depd" "2.0.0"
"encodeurl" "~1.0.2"
"escape-html" "~1.0.3"
"etag" "~1.8.1"
"finalhandler" "1.2.0"
"fresh" "0.5.2"
"http-errors" "2.0.0"
"merge-descriptors" "1.0.1"
"methods" "~1.1.2"
"on-finished" "2.4.1"
"parseurl" "~1.3.3"
"path-to-regexp" "0.1.7"
"proxy-addr" "~2.0.7"
"qs" "6.11.0"
"range-parser" "~1.2.1"
"safe-buffer" "5.2.1"
"send" "0.18.0"
"serve-static" "1.15.0"
"setprototypeof" "1.2.0"
"statuses" "2.0.1"
"type-is" "~1.6.18"
"utils-merge" "1.0.1"
"vary" "~1.1.2"
"fill-range@^7.0.1":
"integrity" "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ=="
"resolved" "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz"
"version" "7.0.1"
dependencies:
"to-regex-range" "^5.0.1"
"finalhandler@1.2.0":
"integrity" "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg=="
"resolved" "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz"
"version" "1.2.0"
dependencies:
"debug" "2.6.9"
"encodeurl" "~1.0.2"
"escape-html" "~1.0.3"
"on-finished" "2.4.1"
"parseurl" "~1.3.3"
"statuses" "2.0.1"
"unpipe" "~1.0.0"
"forwarded@0.2.0":
"integrity" "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="
"resolved" "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz"
"version" "0.2.0"
"fresh@0.5.2":
"integrity" "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="
"resolved" "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz"
"version" "0.5.2"
"function-bind@^1.1.1":
"integrity" "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
"resolved" "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz"
"version" "1.1.1"
"get-intrinsic@^1.0.2":
"integrity" "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q=="
"resolved" "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz"
"version" "1.2.0"
dependencies:
"function-bind" "^1.1.1"
"has" "^1.0.3"
"has-symbols" "^1.0.3"
"glob-parent@~5.1.2":
"integrity" "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="
"resolved" "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz"
"version" "5.1.2"
dependencies:
"is-glob" "^4.0.1"
"has-ansi@^2.0.0":
"integrity" "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg=="
"resolved" "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz"
"version" "2.0.0"
dependencies:
"ansi-regex" "^2.0.0"
"has-flag@^3.0.0":
"integrity" "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="
"resolved" "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz"
"version" "3.0.0"
"has-symbols@^1.0.3":
"integrity" "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
"resolved" "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz"
"version" "1.0.3"
"has@^1.0.3":
"integrity" "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw=="
"resolved" "https://registry.npmjs.org/has/-/has-1.0.3.tgz"
"version" "1.0.3"
dependencies:
"function-bind" "^1.1.1"
"http-errors@2.0.0":
"integrity" "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ=="
"resolved" "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz"
"version" "2.0.0"
dependencies:
"depd" "2.0.0"
"inherits" "2.0.4"
"setprototypeof" "1.2.0"
"statuses" "2.0.1"
"toidentifier" "1.0.1"
"iconv-lite@0.4.24":
"integrity" "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="
"resolved" "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz"
"version" "0.4.24"
dependencies:
"safer-buffer" ">= 2.1.2 < 3"
"ignore-by-default@^1.0.1":
"integrity" "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA=="
"resolved" "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz"
"version" "1.0.1"
"inherits@^2.0.3", "inherits@~2.0.3", "inherits@2.0.4":
"integrity" "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
"resolved" "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz"
"version" "2.0.4"
"ip@^2.0.0":
"integrity" "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ=="
"resolved" "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz"
"version" "2.0.0"
"ipaddr.js@1.9.1":
"integrity" "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
"resolved" "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz"
"version" "1.9.1"
"is-binary-path@~2.1.0":
"integrity" "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw=="
"resolved" "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz"
"version" "2.1.0"
dependencies:
"binary-extensions" "^2.0.0"
"is-extglob@^2.1.1":
"integrity" "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="
"resolved" "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz"
"version" "2.1.1"
"is-glob@^4.0.1", "is-glob@~4.0.1":
"integrity" "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="
"resolved" "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz"
"version" "4.0.3"
dependencies:
"is-extglob" "^2.1.1"
"is-number@^7.0.0":
"integrity" "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="
"resolved" "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz"
"version" "7.0.0"
"isarray@~1.0.0":
"integrity" "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
"resolved" "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz"
"version" "1.0.0"
"js-tokens@^3.0.0 || ^4.0.0":
"integrity" "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
"resolved" "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz"
"version" "4.0.0"
"jsonwebtoken@^9.0.0":
"integrity" "sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw=="
"resolved" "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz"
"version" "9.0.0"
dependencies:
"jws" "^3.2.2"
"lodash" "^4.17.21"
"ms" "^2.1.1"
"semver" "^7.3.8"
"jwa@^1.4.1":
"integrity" "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA=="
"resolved" "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz"
"version" "1.4.1"
dependencies:
"buffer-equal-constant-time" "1.0.1"
"ecdsa-sig-formatter" "1.0.11"
"safe-buffer" "^5.0.1"
"jws@^3.2.2":
"integrity" "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA=="
"resolved" "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz"
"version" "3.2.2"
dependencies:
"jwa" "^1.4.1"
"safe-buffer" "^5.0.1"
"kareem@2.5.1":
"integrity" "sha512-7jFxRVm+jD+rkq3kY0iZDJfsO2/t4BBPeEb2qKn2lR/9KhuksYk5hxzfRYWMPV8P/x2d0kHD306YyWLzjjH+uA=="
"resolved" "https://registry.npmjs.org/kareem/-/kareem-2.5.1.tgz"
"version" "2.5.1"
"lodash@^4.17.21":
"integrity" "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
"resolved" "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz"
"version" "4.17.21"
"loose-envify@^1.1.0", "loose-envify@^1.4.0":
"integrity" "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="
"resolved" "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz"
"version" "1.4.0"
dependencies:
"js-tokens" "^3.0.0 || ^4.0.0"
"lru-cache@^6.0.0":
"integrity" "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="
"resolved" "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz"
"version" "6.0.0"
dependencies:
"yallist" "^4.0.0"
"media-typer@0.3.0":
"integrity" "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="
"resolved" "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz"
"version" "0.3.0"
"memory-pager@^1.0.2":
"integrity" "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg=="
"resolved" "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz"
"version" "1.5.0"
"merge-descriptors@1.0.1":
"integrity" "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
"resolved" "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz"
"version" "1.0.1"
"methods@~1.1.2":
"integrity" "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w=="
"resolved" "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz"
"version" "1.1.2"
"mime-db@1.52.0":
"integrity" "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
"resolved" "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz"
"version" "1.52.0"
"mime-types@~2.1.24", "mime-types@~2.1.34":
"integrity" "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="
"resolved" "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz"
"version" "2.1.35"
dependencies:
"mime-db" "1.52.0"
"mime@1.6.0":
"integrity" "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
"resolved" "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz"
"version" "1.6.0"
"minimatch@^3.1.2":
"integrity" "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="
"resolved" "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz"
"version" "3.1.2"
dependencies:
"brace-expansion" "^1.1.7"
"minimist@^1.2.6":
"integrity" "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="
"resolved" "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz"
"version" "1.2.8"
"mkdirp@^0.5.4":
"integrity" "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw=="
"resolved" "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz"
"version" "0.5.6"
dependencies:
"minimist" "^1.2.6"
"mongodb-connection-string-url@^2.6.0":
"integrity" "sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ=="
"resolved" "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz"
"version" "2.6.0"
dependencies:
"@types/whatwg-url" "^8.2.1"
whatwg-url "^11.0.0"
"whatwg-url" "^11.0.0"
mongodb@5.8.1:
version "5.8.1"
resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-5.8.1.tgz#dc201adfbd6c6d73401cdcf12ebdb75f14771faf"
integrity sha512-wKyh4kZvm6NrCPH8AxyzXm3JBoEf4Xulo0aUWh3hCgwgYJxyQ1KLST86ZZaSWdj6/kxYUA3+YZuyADCE61CMSg==
"mongodb@5.3.0":
"integrity" "sha512-Wy/sbahguL8c3TXQWXmuBabiLD+iVmz+tOgQf+FwkCjhUIorqbAxRbbz00g4ZoN4sXIPwpAlTANMaGRjGGTikQ=="
"resolved" "https://registry.npmjs.org/mongodb/-/mongodb-5.3.0.tgz"
"version" "5.3.0"
dependencies:
bson "^5.4.0"
mongodb-connection-string-url "^2.6.0"
socks "^2.7.1"
"bson" "^5.2.0"
"mongodb-connection-string-url" "^2.6.0"
"socks" "^2.7.1"
optionalDependencies:
"@mongodb-js/saslprep" "^1.1.0"
mongoose@^7.1.1:
version "7.5.0"
resolved "https://registry.yarnpkg.com/mongoose/-/mongoose-7.5.0.tgz#d10003ffc1ff876d761c7cbca6844ddd2aadd42f"
integrity sha512-FpOWOb0AJuaVcplmEyIJ2eCbVGe4gOoniPD+pmft5BrGrNrsFcnYXlERdXtBApGHMHPwD7WbxTyhCbUNr72F3Q==
dependencies:
bson "^5.4.0"
kareem "2.5.1"
mongodb "5.8.1"
mpath "0.9.0"
mquery "5.0.0"
ms "2.1.3"
sift "16.0.1"
mpath@0.9.0:
version "0.9.0"
resolved "https://registry.yarnpkg.com/mpath/-/mpath-0.9.0.tgz#0c122fe107846e31fc58c75b09c35514b3871904"
integrity sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==
mquery@5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/mquery/-/mquery-5.0.0.tgz#a95be5dfc610b23862df34a47d3e5d60e110695d"
integrity sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==
dependencies:
debug "4.x"
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==
ms@2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
ms@2.1.3, ms@^2.1.1:
version "2.1.3"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
multer@^1.4.5-lts.1:
version "1.4.5-lts.1"
resolved "https://registry.yarnpkg.com/multer/-/multer-1.4.5-lts.1.tgz#803e24ad1984f58edffbc79f56e305aec5cfd1ac"
integrity sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==
dependencies:
append-field "^1.0.0"
busboy "^1.0.0"
concat-stream "^1.5.2"
mkdirp "^0.5.4"
object-assign "^4.1.1"
type-is "^1.6.4"
xtend "^4.0.0"
negotiator@0.6.3:
version "0.6.3"
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd"
integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==
nodemailer@^6.9.1:
version "6.9.4"
resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.9.4.tgz#93bd4a60eb0be6fa088a0483340551ebabfd2abf"
integrity sha512-CXjQvrQZV4+6X5wP6ZIgdehJamI63MFoYFGGPtHudWym9qaEHDNdPzaj5bfMCvxG1vhAileSWW90q7nL0N36mA==
nodemon@^2.0.22:
version "2.0.22"
resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.22.tgz#182c45c3a78da486f673d6c1702e00728daf5258"
integrity sha512-B8YqaKMmyuCO7BowF1Z1/mkPqLk6cs/l63Ojtd6otKjMx47Dq1utxfRxcavH1I7VSaL8n5BUaoutadnsX3AAVQ==
dependencies:
chokidar "^3.5.2"
debug "^3.2.7"
ignore-by-default "^1.0.1"
minimatch "^3.1.2"
pstree.remy "^1.1.8"
semver "^5.7.1"
simple-update-notifier "^1.0.7"
supports-color "^5.5.0"
touch "^3.1.0"
undefsafe "^2.0.5"
nopt@~1.0.10:
version "1.0.10"
resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee"
integrity sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==
dependencies:
abbrev "1"
normalize-path@^3.0.0, normalize-path@~3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
object-assign@^4, object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
object-inspect@^1.9.0:
version "1.12.3"
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9"
integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==
on-finished@2.4.1:
version "2.4.1"
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f"
integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==
dependencies:
ee-first "1.1.1"
parseurl@~1.3.3:
version "1.3.3"
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
path-to-regexp@0.1.7:
version "0.1.7"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==
picomatch@^2.0.4, picomatch@^2.2.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
process-nextick-args@~2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
proxy-addr@~2.0.7:
version "2.0.7"
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025"
integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==
dependencies:
forwarded "0.2.0"
ipaddr.js "1.9.1"
pstree.remy@^1.1.8:
version "1.1.8"
resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.8.tgz#c242224f4a67c21f686839bbdb4ac282b8373d3a"
integrity sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==
punycode@^2.1.1:
version "2.3.0"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f"
integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==
qs@6.11.0:
version "6.11.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a"
integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==
dependencies:
side-channel "^1.0.4"
range-parser@~1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
raw-body@2.5.1:
version "2.5.1"
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857"
integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==
dependencies:
bytes "3.1.2"
http-errors "2.0.0"
iconv-lite "0.4.24"
unpipe "1.0.0"
raw-body@2.5.2:
version "2.5.2"
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a"
integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==
dependencies:
bytes "3.1.2"
http-errors "2.0.0"
iconv-lite "0.4.24"
unpipe "1.0.0"
readable-stream@^2.2.2:
version "2.3.8"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b"
integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==
dependencies:
core-util-is "~1.0.0"
inherits "~2.0.3"
isarray "~1.0.0"
process-nextick-args "~2.0.0"
safe-buffer "~5.1.1"
string_decoder "~1.1.1"
util-deprecate "~1.0.1"
readdirp@~3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==
dependencies:
picomatch "^2.2.1"
safe-buffer@5.2.1, safe-buffer@^5.0.1:
version "5.2.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
"saslprep" "^1.0.3"
"mongoose@^7.1.1":
"integrity" "sha512-AIxaWwGY+td7QOMk4NgK6fbRuGovFyDzv65nU1uj1DsUh3lpjfP3iFYHSR+sUKrs7nbp19ksLlRXkmInBteSCA=="
"resolved" "https://registry.npmjs.org/mongoose/-/mongoose-7.1.1.tgz"
"version" "7.1.1"
dependencies:
"bson" "^5.2.0"
"kareem" "2.5.1"
"mongodb" "5.3.0"
"mpath" "0.9.0"
"mquery" "5.0.0"
"ms" "2.1.3"
"sift" "16.0.1"
"mpath@0.9.0":
"integrity" "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew=="
"resolved" "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz"
"version" "0.9.0"
"mquery@5.0.0":
"integrity" "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg=="
"resolved" "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz"
"version" "5.0.0"
dependencies:
"debug" "4.x"
"ms@^2.1.1":
"integrity" "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
"resolved" "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz"
"version" "2.1.3"
"ms@2.0.0":
"integrity" "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
"resolved" "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz"
"version" "2.0.0"
"ms@2.1.2":
"integrity" "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
"resolved" "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz"
"version" "2.1.2"
"ms@2.1.3":
"integrity" "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
"resolved" "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz"
"version" "2.1.3"
"multer@^1.4.5-lts.1":
"integrity" "sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ=="
"resolved" "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz"
"version" "1.4.5-lts.1"
dependencies:
"append-field" "^1.0.0"
"busboy" "^1.0.0"
"concat-stream" "^1.5.2"
"mkdirp" "^0.5.4"
"object-assign" "^4.1.1"
"type-is" "^1.6.4"
"xtend" "^4.0.0"
"negotiator@0.6.3":
"integrity" "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="
"resolved" "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz"
"version" "0.6.3"
"nodemailer@^6.9.1":
"integrity" "sha512-qHw7dOiU5UKNnQpXktdgQ1d3OFgRAekuvbJLcdG5dnEo/GtcTHRYM7+UfJARdOFU9WUQO8OiIamgWPmiSFHYAA=="
"resolved" "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.1.tgz"
"version" "6.9.1"
"nodemon@^2.0.22":
"integrity" "sha512-B8YqaKMmyuCO7BowF1Z1/mkPqLk6cs/l63Ojtd6otKjMx47Dq1utxfRxcavH1I7VSaL8n5BUaoutadnsX3AAVQ=="
"resolved" "https://registry.npmjs.org/nodemon/-/nodemon-2.0.22.tgz"
"version" "2.0.22"
dependencies:
"chokidar" "^3.5.2"
"debug" "^3.2.7"
"ignore-by-default" "^1.0.1"
"minimatch" "^3.1.2"
"pstree.remy" "^1.1.8"
"semver" "^5.7.1"
"simple-update-notifier" "^1.0.7"
"supports-color" "^5.5.0"
"touch" "^3.1.0"
"undefsafe" "^2.0.5"
"nopt@~1.0.10":
"integrity" "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg=="
"resolved" "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz"
"version" "1.0.10"
dependencies:
"abbrev" "1"
"normalize-path@^3.0.0", "normalize-path@~3.0.0":
"integrity" "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="
"resolved" "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz"
"version" "3.0.0"
"object-assign@^4", "object-assign@^4.1.1":
"integrity" "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="
"resolved" "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz"
"version" "4.1.1"
"object-inspect@^1.9.0":
"integrity" "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g=="
"resolved" "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz"
"version" "1.12.3"
"on-finished@2.4.1":
"integrity" "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="
"resolved" "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz"
"version" "2.4.1"
dependencies:
"ee-first" "1.1.1"
"parseurl@~1.3.3":
"integrity" "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
"resolved" "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz"
"version" "1.3.3"
"path-to-regexp@0.1.7":
"integrity" "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
"resolved" "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz"
"version" "0.1.7"
"picomatch@^2.0.4", "picomatch@^2.2.1":
"integrity" "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="
"resolved" "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz"
"version" "2.3.1"
"process-nextick-args@~2.0.0":
"integrity" "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
"resolved" "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz"
"version" "2.0.1"
"prop-types@^15.5.10", "prop-types@^15.6.0", "prop-types@^15.6.2":
"integrity" "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="
"resolved" "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz"
"version" "15.8.1"
dependencies:
"loose-envify" "^1.4.0"
"object-assign" "^4.1.1"
"react-is" "^16.13.1"
"proxy-addr@~2.0.7":
"integrity" "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="
"resolved" "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz"
"version" "2.0.7"
dependencies:
"forwarded" "0.2.0"
"ipaddr.js" "1.9.1"
"pstree.remy@^1.1.8":
"integrity" "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w=="
"resolved" "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz"
"version" "1.1.8"
"punycode@^2.1.1":
"integrity" "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA=="
"resolved" "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz"
"version" "2.3.0"
"qs@6.11.0":
"integrity" "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q=="
"resolved" "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz"
"version" "6.11.0"
dependencies:
"side-channel" "^1.0.4"
"range-parser@~1.2.1":
"integrity" "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
"resolved" "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz"
"version" "1.2.1"
"raw-body@2.5.1":
"integrity" "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig=="
"resolved" "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz"
"version" "2.5.1"
dependencies:
"bytes" "3.1.2"
"http-errors" "2.0.0"
"iconv-lite" "0.4.24"
"unpipe" "1.0.0"
"raw-body@2.5.2":
"integrity" "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA=="
"resolved" "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz"
"version" "2.5.2"
dependencies:
"bytes" "3.1.2"
"http-errors" "2.0.0"
"iconv-lite" "0.4.24"
"unpipe" "1.0.0"
"react-ga@^2.2.0":
"integrity" "sha512-AjC7UOZMvygrWTc2hKxTDvlMXEtbmA0IgJjmkhgmQQ3RkXrWR11xEagLGFGaNyaPnmg24oaIiaNPnEoftUhfXA=="
"resolved" "https://registry.npmjs.org/react-ga/-/react-ga-2.7.0.tgz"
"version" "2.7.0"
"react-is@^16.13.1":
"integrity" "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
"resolved" "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz"
"version" "16.13.1"
"react-mic@^12.4.6":
"integrity" "sha512-2/DZoz7thR2nJyekF10zBvs/7a8HhUQ4L8MV6BpC+Q/T8G1MvpRHGSHjSlVtnbzaCMDJ3R1MdThoLu15WuVh/g=="
"resolved" "https://registry.npmjs.org/react-mic/-/react-mic-12.4.6.tgz"
"version" "12.4.6"
dependencies:
"prop-types" "^15.5.10"
"react-ga" "^2.2.0"
"react@^15.6.2 || ^16.0", "react@16.x":
"integrity" "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g=="
"resolved" "https://registry.npmjs.org/react/-/react-16.14.0.tgz"
"version" "16.14.0"
dependencies:
"core-util-is" "~1.0.0"
"inherits" "~2.0.3"
"isarray" "~1.0.0"
"process-nextick-args" "~2.0.0"
"safe-buffer" "~5.1.1"
"string_decoder" "~1.1.1"
"util-deprecate" "~1.0.1"
"readdirp@~3.6.0":
"integrity" "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="
"resolved" "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz"
"version" "3.6.0"
dependencies:
"picomatch" "^2.2.1"
"safe-buffer@^5.0.1", "safe-buffer@5.2.1":
"integrity" "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
"resolved" "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz"
"version" "5.2.1"
"safe-buffer@~5.1.0":
"integrity" "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
"resolved" "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz"
"version" "5.1.2"
"safe-buffer@~5.1.1":
"integrity" "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
"resolved" "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz"
"version" "5.1.2"
"safer-buffer@>= 2.1.2 < 3":
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
semver@^5.7.1:
version "5.7.2"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8"
integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==
semver@^7.3.8:
version "7.5.4"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e"
integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==
dependencies:
lru-cache "^6.0.0"
semver@~7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e"
integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==
send@0.18.0:
version "0.18.0"
resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be"
integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==
dependencies:
debug "2.6.9"
depd "2.0.0"
destroy "1.2.0"
encodeurl "~1.0.2"
escape-html "~1.0.3"
etag "~1.8.1"
fresh "0.5.2"
http-errors "2.0.0"
mime "1.6.0"
ms "2.1.3"
on-finished "2.4.1"
range-parser "~1.2.1"
statuses "2.0.1"
serve-static@1.15.0:
version "1.15.0"
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540"
integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==
dependencies:
encodeurl "~1.0.2"
escape-html "~1.0.3"
parseurl "~1.3.3"
send "0.18.0"
setprototypeof@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
side-channel@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf"
integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==
dependencies:
call-bind "^1.0.0"
get-intrinsic "^1.0.2"
object-inspect "^1.9.0"
sift@16.0.1:
version "16.0.1"
resolved "https://registry.yarnpkg.com/sift/-/sift-16.0.1.tgz#e9c2ccc72191585008cf3e36fc447b2d2633a053"
integrity sha512-Wv6BjQ5zbhW7VFefWusVP33T/EM0vYikCaQ2qR8yULbsilAT8/wQaXvuQ3ptGLpoKx+lihJE3y2UTgKDyyNHZQ==
simple-update-notifier@^1.0.7:
version "1.1.0"
resolved "https://registry.yarnpkg.com/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz#67694c121de354af592b347cdba798463ed49c82"
integrity sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==
dependencies:
semver "~7.0.0"
smart-buffer@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae"
integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==
socks@^2.7.1:
version "2.7.1"
resolved "https://registry.yarnpkg.com/socks/-/socks-2.7.1.tgz#d8e651247178fde79c0663043e07240196857d55"
integrity sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==
dependencies:
ip "^2.0.0"
smart-buffer "^4.2.0"
sparse-bitfield@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz#ff4ae6e68656056ba4b3e792ab3334d38273ca11"
integrity sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==
dependencies:
memory-pager "^1.0.2"
statuses@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"
integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==
streamsearch@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764"
integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==
string_decoder@~1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
dependencies:
safe-buffer "~5.1.0"
strip-ansi@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
integrity sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==
dependencies:
ansi-regex "^2.0.0"
supports-color@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
integrity sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==
supports-color@^5.5.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
dependencies:
has-flag "^3.0.0"
to-regex-range@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
dependencies:
is-number "^7.0.0"
toidentifier@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==
torch@^0.2.7:
version "0.2.7"
resolved "https://registry.yarnpkg.com/torch/-/torch-0.2.7.tgz#ac4bc487e988ee582b383ece48fe0169e95443ad"
integrity sha512-yTv7qWKGg00hMDv0pyBgRjubbf4eygzzrjKPKRC9rbPCKBF0jd+cxnzIoN+pCHgGf2EQbd0jGyy1X7h5BIqjEA==
dependencies:
chalk "^1.1.3"
touch@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.0.tgz#fe365f5f75ec9ed4e56825e0bb76d24ab74af83b"
integrity sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==
dependencies:
nopt "~1.0.10"
tr46@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/tr46/-/tr46-3.0.0.tgz#555c4e297a950617e8eeddef633c87d4d9d6cbf9"
integrity sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==
dependencies:
punycode "^2.1.1"
type-is@^1.6.4, type-is@~1.6.18:
version "1.6.18"
resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==
dependencies:
media-typer "0.3.0"
mime-types "~2.1.24"
typedarray@^0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==
undefsafe@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c"
integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==
unpipe@1.0.0, unpipe@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==
util-deprecate@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
utils-merge@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==
uuid@^9.0.0:
version "9.0.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5"
integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==
vary@^1, vary@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==
webidl-conversions@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a"
integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==
whatwg-url@^11.0.0:
version "11.0.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-11.0.0.tgz#0a849eebb5faf2119b901bb76fd795c2848d4018"
integrity sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==
dependencies:
tr46 "^3.0.0"
webidl-conversions "^7.0.0"
xtend@^4.0.0:
version "4.0.2"
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
yallist@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
"integrity" "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
"resolved" "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz"
"version" "2.1.2"
"saslprep@^1.0.3":
"integrity" "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag=="
"resolved" "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz"
"version" "1.0.3"
dependencies:
"sparse-bitfield" "^3.0.3"
"semver@^5.7.1":
"integrity" "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
"resolved" "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz"
"version" "5.7.1"
"semver@^7.3.8":
"integrity" "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA=="
"resolved" "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz"
"version" "7.5.0"
dependencies:
"lru-cache" "^6.0.0"
"semver@~7.0.0":
"integrity" "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A=="
"resolved" "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz"
"version" "7.0.0"
"send@0.18.0":
"integrity" "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg=="
"resolved" "https://registry.npmjs.org/send/-/send-0.18.0.tgz"
"version" "0.18.0"
dependencies:
"debug" "2.6.9"
"depd" "2.0.0"
"destroy" "1.2.0"
"encodeurl" "~1.0.2"
"escape-html" "~1.0.3"
"etag" "~1.8.1"
"fresh" "0.5.2"
"http-errors" "2.0.0"
"mime" "1.6.0"
"ms" "2.1.3"
"on-finished" "2.4.1"
"range-parser" "~1.2.1"
"statuses" "2.0.1"
"serve-static@1.15.0":
"integrity" "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g=="
"resolved" "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz"
"version" "1.15.0"
dependencies:
"encodeurl" "~1.0.2"
"escape-html" "~1.0.3"
"parseurl" "~1.3.3"
"send" "0.18.0"
"setprototypeof@1.2.0":
"integrity" "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
"resolved" "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz"
"version" "1.2.0"
"side-channel@^1.0.4":
"integrity" "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw=="
"resolved" "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz"
"version" "1.0.4"
dependencies:
"call-bind" "^1.0.0"
"get-intrinsic" "^1.0.2"
"object-inspect" "^1.9.0"
"sift@16.0.1":
"integrity" "sha512-Wv6BjQ5zbhW7VFefWusVP33T/EM0vYikCaQ2qR8yULbsilAT8/wQaXvuQ3ptGLpoKx+lihJE3y2UTgKDyyNHZQ=="
"resolved" "https://registry.npmjs.org/sift/-/sift-16.0.1.tgz"
"version" "16.0.1"
"simple-update-notifier@^1.0.7":
"integrity" "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg=="
"resolved" "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz"
"version" "1.1.0"
dependencies:
"semver" "~7.0.0"
"smart-buffer@^4.2.0":
"integrity" "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg=="
"resolved" "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz"
"version" "4.2.0"
"socks@^2.7.1":
"integrity" "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ=="
"resolved" "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz"
"version" "2.7.1"
dependencies:
"ip" "^2.0.0"
"smart-buffer" "^4.2.0"
"sparse-bitfield@^3.0.3":
"integrity" "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ=="
"resolved" "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz"
"version" "3.0.3"
dependencies:
"memory-pager" "^1.0.2"
"statuses@2.0.1":
"integrity" "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="
"resolved" "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz"
"version" "2.0.1"
"streamsearch@^1.1.0":
"integrity" "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg=="
"resolved" "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz"
"version" "1.1.0"
"string_decoder@~1.1.1":
"integrity" "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="
"resolved" "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz"
"version" "1.1.1"
dependencies:
"safe-buffer" "~5.1.0"
"strip-ansi@^3.0.0":
"integrity" "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg=="
"resolved" "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz"
"version" "3.0.1"
dependencies:
"ansi-regex" "^2.0.0"
"supports-color@^2.0.0":
"integrity" "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g=="
"resolved" "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz"
"version" "2.0.0"
"supports-color@^5.5.0":
"integrity" "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow=="
"resolved" "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz"
"version" "5.5.0"
dependencies:
"has-flag" "^3.0.0"
"to-regex-range@^5.0.1":
"integrity" "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="
"resolved" "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz"
"version" "5.0.1"
dependencies:
"is-number" "^7.0.0"
"toidentifier@1.0.1":
"integrity" "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="
"resolved" "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz"
"version" "1.0.1"
"torch@^0.2.7":
"integrity" "sha512-yTv7qWKGg00hMDv0pyBgRjubbf4eygzzrjKPKRC9rbPCKBF0jd+cxnzIoN+pCHgGf2EQbd0jGyy1X7h5BIqjEA=="
"resolved" "https://registry.npmjs.org/torch/-/torch-0.2.7.tgz"
"version" "0.2.7"
dependencies:
"chalk" "^1.1.3"
"touch@^3.1.0":
"integrity" "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA=="
"resolved" "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz"
"version" "3.1.0"
dependencies:
"nopt" "~1.0.10"
"tr46@^3.0.0":
"integrity" "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA=="
"resolved" "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz"
"version" "3.0.0"
dependencies:
"punycode" "^2.1.1"
"type-is@^1.6.4", "type-is@~1.6.18":
"integrity" "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g=="
"resolved" "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz"
"version" "1.6.18"
dependencies:
"media-typer" "0.3.0"
"mime-types" "~2.1.24"
"typedarray@^0.0.6":
"integrity" "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="
"resolved" "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz"
"version" "0.0.6"
"undefsafe@^2.0.5":
"integrity" "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA=="
"resolved" "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz"
"version" "2.0.5"
"unpipe@~1.0.0", "unpipe@1.0.0":
"integrity" "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="
"resolved" "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz"
"version" "1.0.0"
"util-deprecate@~1.0.1":
"integrity" "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
"resolved" "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz"
"version" "1.0.2"
"utils-merge@1.0.1":
"integrity" "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="
"resolved" "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz"
"version" "1.0.1"
"uuid@^9.0.0":
"integrity" "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg=="
"resolved" "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz"
"version" "9.0.0"
"vary@^1", "vary@~1.1.2":
"integrity" "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="
"resolved" "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz"
"version" "1.1.2"
"webidl-conversions@^7.0.0":
"integrity" "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="
"resolved" "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz"
"version" "7.0.0"
"whatwg-url@^11.0.0":
"integrity" "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ=="
"resolved" "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz"
"version" "11.0.0"
dependencies:
"tr46" "^3.0.0"
"webidl-conversions" "^7.0.0"
"xtend@^4.0.0":
"integrity" "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
"resolved" "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz"
"version" "4.0.2"
"yallist@^4.0.0":
"integrity" "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
"resolved" "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz"
"version" "4.0.0"
import os
from fastapi import APIRouter, File, HTTPException, Query,UploadFile
from pydantic import BaseModel
import tensorflow as tf
......@@ -11,12 +12,22 @@ from utils import mappings
router = APIRouter()
logger = setup_logger()
class ImageRequest(BaseModel):
image: UploadFile
# Load your Keras model
# model = tf.keras.models.load_model('../ML_Models/sign_language_to_text/models/sign_language_model.h5')
model= None
model_file_path = '../ML_Models/sign_language_to_text/models/sign_language_model.h5'
# Check if the model file exists
if os.path.exists(model_file_path):
# Load the Keras model if the file exists
model = tf.keras.models.load_model(model_file_path)
else:
model = None
CLASSES = mappings.classes
NUM_CLASSES = len(mappings.classes) # number of classes
IMG_SIZE = 224 # image size
......@@ -29,7 +40,6 @@ prediction_service = SignLanguagePredictionService(model, CLASSES, mappings,spee
@router.post("/upload/video", tags=["Sign Language"])
async def upload_video(video: UploadFile = File(...)):
try:
file_location = f"files/{video.filename}"
with open(file_location, "wb") as file:
file.write(video.file.read())
......@@ -69,7 +79,4 @@ def predict_using_video(video_request: UploadFile = File(...), speed: int = Quer
return prediction_service.predict_sign_language_video_with_speed_levels(video_request, speed=speed)
except Exception as e:
logger.info(f"Error. {e}")
raise HTTPException(
status_code=500,
detail="Request Failed."
)
\ No newline at end of file
raise HTTPException(status_code=500, detail="Request Failed.")
from datetime import datetime
import moviepy.editor as mp
import requests
import speech_recognition as sr
from fastapi import APIRouter, File, HTTPException, UploadFile
from fastapi.responses import JSONResponse
from pymongo.mongo_client import MongoClient
from bson import ObjectId
from core.logger import setup_logger
# Replace with your MongoDB Atlas credentials
username = "admin"
password = "JppbU6MZeHfOj7sp"
uri = f"mongodb+srv://{username}:{password}@researchmanagement-appl.vzhn4.mongodb.net/?retryWrites=true&w=majority"
client = MongoClient(uri)
db = client["test"]
items_collection = db["translated_items"]
items_collection_log = db["translated_items_log"]
TEMP_VIDEO_PATH = "C:/Users/himashara/Documents/SLIIT/1 SEMESTER/Research Project - IT4010/Research Project/2023-029/Project/Backend/Server_Python/resources/temp_video.mp4"
AUDIO_SAVE_PATH = "C:/Users/himashara/Documents/SLIIT/1 SEMESTER/Research Project - IT4010/Research Project/2023-029/Project/Backend/Server_Python/resources/audio.wav"
router = APIRouter()
logger = setup_logger()
# @router.get("/rest_pyton/items")
# async def read_root():
# return {"message": "Hello, World!"}
@router.get("/rest_pyton/items")
async def read_items():
items = []
for item in items_collection.find():
item_dict = dict(item)
item_dict['_id'] = str(item_dict['_id']) # Convert _id to a string
items.append(item_dict)
return {"items": items}
@router.post("/rest_pyton/uploaded_video")
async def uploaded_video(file: UploadFile = File(...)):
try:
# Process the uploaded video
video_content = await file.read()
temp_video_path = TEMP_VIDEO_PATH
with open(temp_video_path, "wb") as temp_video_file:
temp_video_file.write(video_content)
audio_save_path = AUDIO_SAVE_PATH
video = mp.VideoFileClip(temp_video_path)
audio = video.audio
audio.write_audiofile(audio_save_path)
r = sr.Recognizer()
with sr.AudioFile(audio_save_path) as source:
audio = r.record(source)
recognized_text = r.recognize_google(audio, language="si-LK")
translated_text_si = translate_text(recognized_text, "si")
translated_text_en = translate_text(recognized_text, "en")
translated_integer_si = " ".join(
str(unicode_to_int_mapping.get(word, "0"))
for word in translated_text_si.split()
)
print("Translated Integer (Si):", translated_integer_si)
# Send translated integer to MongoDB
send_to_mongodb(translated_integer_si)
return JSONResponse(
content={
"translated_text_si": translated_text_si,
"translated_text_en": translated_text_en,
}
)
# return JSONResponse(content={"translated_text_si": "test"})
except Exception as e:
return JSONResponse(content={"error": str(e)}, status_code=500)
unicode_to_int_mapping = {"මම": 1, "හෙට": 2, "යනවා": 3, "මං": 4}
def translate_text(text, target_language):
url = "https://translate.googleapis.com/translate_a/single"
params = {
"client": "gtx",
"sl": "auto",
"tl": target_language,
"dt": "t",
"q": text,
}
response = requests.get(url, params=params)
translation_data = response.json()
translated_text = translation_data[0][0][0]
return translated_text
# v1
# def send_to_mongodb(translated_integer_si):
# translated_item_data = {
# "translated_integer_si": translated_integer_si,
# "timestamp": datetime.utcnow(),
# }
# result = items_collection.insert_one(translated_item_data)
# if not result.inserted_id:
# raise HTTPException(status_code=500, detail="Failed to create translated item")
# v2
# def send_to_mongodb(translated_integer_si):
# translated_item_data = {
# "translated_integer_si": translated_integer_si,
# "timestamp": datetime.utcnow(),
# }
# # Save the previous record to the log before updating
# existing_record = items_collection.find_one()
# if existing_record:
# items_collection_log = db["translated_items_log"]
# items_collection_log.insert_one(existing_record)
# # Update the existing record or create a new one
# result = items_collection.replace_one({}, translated_item_data, upsert=True)
# if result.matched_count == 0 and result.modified_count == 0:
# raise HTTPException(
# status_code=500, detail="Failed to update or create translated item"
# )
# v3
def send_to_mongodb(translated_integer_si):
translated_item_data = {
"translated_integer_si": translated_integer_si,
"timestamp": datetime.utcnow(),
}
# Save the previous record to the log before updating
existing_record = items_collection.find_one()
if existing_record:
items_collection_log = db["translated_items_log"]
# Exclude the _id field to allow MongoDB to generate a new one
existing_record.pop("_id", None)
items_collection_log.insert_one(existing_record)
# Update the existing record or create a new one
result = items_collection.replace_one({}, translated_item_data, upsert=True)
if result.matched_count == 0 and result.modified_count == 0:
raise HTTPException(
status_code=500, detail="Failed to update or create translated item"
)
from fastapi import FastAPI, HTTPException
from pymongo.mongo_client import MongoClient
from pydantic import BaseModel
from typing import List
from bson import ObjectId
from datetime import datetime
app = FastAPI()
# Replace with your MongoDB Atlas credentials
username = "admin"
password = "JppbU6MZeHfOj7sp"
uri = f"mongodb+srv://{username}:{password}@researchmanagement-appl.vzhn4.mongodb.net/?retryWrites=true&w=majority"
client = MongoClient(uri)
db = client["test"]
items_collection = db["translated_items"]
class ItemCreate(BaseModel):
item_name: str
item_description: str
class Item(BaseModel):
item_name: str
item_description: str
_id: str
class TranslatedItemCreate(BaseModel):
translated_integer_si: str
@app.on_event("startup")
async def startup_db_client():
app.mongodb_client = MongoClient(uri)
try:
app.mongodb_client.admin.command("ping")
print("Pinged your deployment. You successfully connected to MongoDB!")
except Exception as e:
print(e)
@app.on_event("shutdown")
async def shutdown_db_client():
app.mongodb_client.close()
@app.get("/")
async def read_root():
return {"message": "FastAPI with MongoDB integration"}
# send tranlated integer to mongodb
@app.post("/translated_items/", response_model=TranslatedItemCreate)
async def create_translated_item(translated_item_create: TranslatedItemCreate):
translated_item_data = {
"translated_integer_si": translated_item_create.translated_integer_si,
"timestamp": datetime.utcnow(),
}
result = items_collection.insert_one(translated_item_data)
if result.inserted_id:
# return translated_item_data
return {**translated_item_data, "_id": str(result.inserted_id)}
else:
raise HTTPException(status_code=500, detail="Failed to create translated item")
@app.put("/items/{item_id}", response_model=Item)
async def update_item(item_id: str, item_update: ItemCreate):
item_object_id = ObjectId(item_id) # Convert item_id to ObjectId
update_data = {
"item_name": item_update.item_name,
"item_description": item_update.item_description,
}
result = items_collection.update_one({"_id": item_object_id}, {"$set": update_data})
if result.modified_count > 0:
updated_item = items_collection.find_one({"_id": item_object_id})
return {**updated_item, "_id": str(updated_item["_id"])}
else:
raise HTTPException(status_code=404, detail="Item not found")
@app.get("/items/", response_model=List[Item])
async def get_all_items():
items = list(items_collection.find())
items_with_string_id = [{**item, "_id": str(item["_id"])} for item in items]
print(items_with_string_id)
return items_with_string_id
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="127.0.0.1", port=8000)
from fastapi import FastAPI
from controllers import translate_controler, users_controller
from controllers import (
translate_controler,
users_controller,
video_to_sign_language_controller,
)
from fastapi.responses import RedirectResponse
from fastapi.middleware.cors import CORSMiddleware
from pymongo.mongo_client import MongoClient
from core.logger import setup_logger
app = FastAPI()
# Replace with your MongoDB Atlas credentials
username = "admin"
password = "JppbU6MZeHfOj7sp"
uri = f"mongodb+srv://{username}:{password}@researchmanagement-appl.vzhn4.mongodb.net/?retryWrites=true&w=majority"
client = MongoClient(uri)
db = client["test"]
items_collection = db["translated_items"]
@app.on_event("startup")
async def startup_db_client():
app.mongodb_client = MongoClient(uri)
try:
app.mongodb_client.admin.command("ping")
print("Pinged your deployment. You successfully connected to MongoDB!")
except Exception as e:
print(e)
@app.on_event("shutdown")
async def shutdown_db_client():
app.mongodb_client.close()
app = FastAPI()
logger = setup_logger()
app.include_router(users_controller.router)
app.include_router(translate_controler.router)
app.include_router(video_to_sign_language_controller.router)
# Add cores middleware
......@@ -21,15 +50,26 @@ origins = [
"http://localhost:8080",
"http://localhost:8004",
"http://localhost:3000",
"http://127.0.0.1:8000",
"127.0.0.1:55553",
"http://localhost:52823",
"http://localhost:53826",
"http://localhost:51373",
"http://localhost:51489",
"https://v6p9d9t4.ssl.hwcdn.net",
"https://64f66d39fdef493229b2ddd9--lambent-unicorn-97396a.netlify.app"
]
app.add_middleware(CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"])
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.get('/')
@app.get("/")
async def root():
url = app.docs_url or '/docs'
return RedirectResponse(url)
\ No newline at end of file
url = app.docs_url or "/docs"
return RedirectResponse(url)
{
"systemParams": "win32-x64-93",
"modulesFolders": [],
"flags": [],
"linkedModules": [],
"topLevelPatterns": [],
"lockfileEntries": {},
"files": [],
"artifacts": {}
}
\ No newline at end of file
......@@ -2,4 +2,9 @@ fastapi
uvicorn
python-multipart==0.0.6
tensorflow==2.12.0
opencv-python==4.7.0.72
\ No newline at end of file
opencv-python==4.7.0.72
moviepy==1.0.3
SpeechRecognition==3.10.0
tk==0.1.0
requests==2.31.0
pymongo==4.5.0
\ No newline at end of file
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
......@@ -96,6 +96,7 @@
"react-table-sticky": "^1.1.3",
"react-timer-hook": "^3.0.5",
"react-to-print": "^2.14.12",
"react-unity-webgl": "^9.4.3",
"react-webcam": "^7.1.1",
"react-window": "^1.8.9",
"react-zoom-pan-pinch": "^3.0.7",
......
......@@ -56,17 +56,27 @@ export const JWTProvider = ({ children }: { children: React.ReactElement }) => {
const init = async () => {
try {
const serviceToken = window.localStorage.getItem('serviceToken');
console.log(verifyToken(serviceToken!));
if (serviceToken && verifyToken(serviceToken)) {
setSession(serviceToken);
// const response = await axios.get('/api/account/me');
// const { user } = response.data;
// Set the token in your Axios instance for future requests
axiosServices.defaults.headers.common['Authorization'] = `Bearer ${serviceToken}`;
// Make the API request
const response = await axiosServices.get('/rest_node/user/current-user');
const { user } = response.data;
dispatch({
type: LOGIN,
payload: {
isLoggedIn: true,
// user
user: {
id: user._id,
email: user.email,
name: `${user.firstName} ${user.lastName}`,
role: user.type
}
}
});
} else {
......
import { curriculumType } from "types/curriculum";
export const curriculums: curriculumType[] = [
{
"curriculumCode": "01",
"curriculumLevel": 1,
"curriculumDescription": "This curriculum teaches basic sign language skills to help beginners communicate effectively using sign language.",
"curriculumTitle": "Basic Sign Language Skills",
"curriculumImage": "https://drive.google.com/uc?export=view&id=1YACBlu7X-O7-DKv5DoW3AM9kgfT7Yhdc",
"_id": "1",
"status": 1,
"createdBy": "Nuwan Gamage",
"createdAt": new Date("2023-08-30T12:00:00Z"),
"curriculumMark": 100,
"tutorials": [
{
"tutorialCode": "01",
"tutorialTitle": "Numbers and Counting",
"tutorialDescription": "In this tutorial, you'll discover how to express numbers visually using simple hand gestures. Each number has a unique sign that involves specific finger placements and hand movements. We'll break down each number step by step, providing you with clear instructions, images, and videos to help you learn effectively.",
"tutorialImage": "https://drive.google.com/uc?export=view&id=1GeFzoy3xt8UnfCQE3IPVjPXoAg7GAWgf",
"_id": "1",
"status": 1,
"createdBy": "Nuwan Gamage",
"createdAt": new Date("2023-08-30T12:00:00Z"),
"tutorialMark": 100,
"taskItems": [
{
"_id": "1",
"title": "Learn Number One",
"description": "In this lesson, you will learn how to sign the number one. Understanding this basic sign is crucial for counting and expressing singular items or concepts in sign language. Practice the hand shape, movement, and facial expression associated with the sign to improve your fluency.",
"howToDo": [
"- Extend your index finger straight up.",
"- Keep the rest of your fingers closed.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=17sHGfW9zip8xAwbRtUihzxkseKq-Qn7q",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "2",
"title": "Learn Number Two",
"description": "In this lesson, you will learn how to sign the number two. Mastering this sign is essential for counting and expressing pairs or dual concepts in sign language. Pay attention to the hand shape, movement, and facial expression involved in signing the number two to enhance your signing skills.",
"howToDo": [
"- Extend your index and middle fingers straight up.",
"- Keep the rest of your fingers closed.",
"- Hold your hand in front of your chest."],
"referenceImage": "https://drive.google.com/uc?export=view&id=1Nm-we15Rrr04ovRC1sr-ZVMNHbnALRm2",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "3",
"title": "Learn Number Three",
"description": "In this lesson, you will learn how to sign the number three in sign language. Mastering this sign is essential for expressing the quantity or position of three items. Pay attention to the hand shape, movement, and facial expression involved in signing the number three to enhance your signing skills.",
"howToDo": [
"- Extend your index, middle, and ring fingers straight up.",
"- Keep the rest of your fingers closed.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1mKY8D1utbqm8flnNM7DZRKtuPQDXqFSm",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "4",
"title": "Learn Number Four",
"description": "In this lesson, you will learn how to sign the number four in sign language. This sign is commonly used for counting or indicating the quantity or position of four items. Focus on the hand shape, movement, and expression to accurately convey the number four in sign language.",
"howToDo": [
"- Extend your thumb, index, middle, and ring fingers straight up.",
"- Keep your pinky finger folded.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1VtbHehsxbOC04fVR6_Ca6HDv6csH17SJ",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "5",
"title": "Learn Number Five",
"description": "In this lesson, you will learn how to sign the number five in sign language. Mastering this sign is crucial for counting, expressing quantities, or representing the concept of five. Practice the hand shape, movement, and facial expression to effectively communicate the number five in sign language.",
"howToDo": [
"- Extend all your fingers straight up.",
"- Keep your thumb resting on the side of your palm.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1G6qx4dhHTKUPvNag0R3qlDJugNOgcTqM",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "6",
"title": "Learn Number Six",
"description": "In this lesson, you will learn how to sign the number six in sign language. This sign is commonly used for counting, indicating quantities, or representing the concept of six. Pay attention to the hand shape, movement, and expression involved in signing the number six to enhance your signing proficiency.",
"howToDo": [
"- Extend your thumb and pinky finger straight up.",
"- Keep the rest of your fingers closed.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1Q2TbcPTx8KuLp4v2mPL_7GHO0l52DZXw",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "7",
"title": "Learn Number Seven",
"description": "In this lesson, you will learn how to sign the number seven in sign language. Mastering this sign is essential for counting, expressing quantities, or representing the concept of seven. Focus on the hand shape, movement, and facial expression to accurately convey the number seven in sign language.",
"howToDo": [
"- Extend your index, middle, and ring fingers straight up.",
"- Keep your thumb, pinky, and pinky finger folded.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1Jwt1TqXO1L7t3JFqQYzKL4S4GCuqULJ1",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "8",
"title": "Learn Number Eight",
"description": "In this lesson, you will learn how to sign the number eight in sign language. This sign is commonly used for counting, indicating quantities, or representing the concept of eight. Practice the hand shape, movement, and expression to effectively communicate the number eight in sign language.",
"howToDo": [
"- Extend all your fingers straight up.",
"- Cross your index and middle fingers over your ring and pinky fingers.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1zSf4hmqIUCcfebgoD6DTWmJ3NxY36LJL",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "9",
"title": "Learn Number Nine",
"description": "In this lesson, you will learn how to sign the number nine in sign language. Mastering this sign is crucial for counting, expressing quantities, or representing the concept of nine. Pay attention to the hand shape, movement, and expression involved in signing the number nine to enhance your signing proficiency.",
"howToDo": [
"- Extend your thumb and all your fingers straight up.",
"- Keep your pinky finger folded.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=16WB1Ifhq_ozKOO-ehfIqVRB6oC3B5STw",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "10",
"title": "Learn Number Ten",
"description": "In this lesson, you will learn how to sign the number ten in sign language. This sign is commonly used for counting, indicating quantities, or representing the concept of ten. Focus on the hand shape, movement, and facial expression to accurately convey the number ten in sign language.",
"howToDo": [
"- Extend your thumb, index, and middle fingers straight up.",
"- Keep the rest of your fingers closed.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=11tCYFYjdVrr5LIFGyzOrIUg8bTURATZh",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
}
]
},
{
"tutorialCode": "02",
"tutorialTitle": "Everyday Vocabulary",
"tutorialDescription": "Teach signs for everyday objects and activities, such as eat, drink, sleep, book, pen, etc.\nIntroduce signs for common words used in daily life.\nProvide visual demonstrations and interactive exercises for learners to practice using these signs.",
"tutorialImage": "https://drive.google.com/uc?export=view&id=1QqmeBBiAojz7jaHUUdQGLyqUVR-mKSsy",
"taskItems": [],
"_id": "2",
"status": 1,
"createdBy": "Nuwan Gamage",
"createdAt": new Date("2023-08-30T12:00:00Z"),
"tutorialMark": 0,
},
{
"tutorialCode": "03",
"tutorialTitle": "Family Signs",
"tutorialDescription": "Teach signs for family members, such as mother, father, sister, brother, etc.\nIntroduce signs for common family-related words, such as family, love, and home.\nProvide visual demonstrations and practice exercises for learners to practice these family signs.",
"tutorialImage": "https://drive.google.com/uc?export=view&id=1_b3-0HAAWu5Ze20IAAKWxUSUS-fac6Dg",
"taskItems": [],
"_id": "3",
"status": 1,
"createdBy": "Nuwan Gamage",
"createdAt": new Date("2023-08-30T12:00:00Z"),
"tutorialMark": 0,
},
{
"tutorialCode": "04",
"tutorialTitle": "Basic Conversational Phrases",
"tutorialDescription": "Teach simple conversational phrases, such as \"What is your name?\" or \"How are you?\"\nIntroduce signs for common question words and phrases.\nProvide visual demonstrations and practice exercises for learners to practice these conversational phrases.",
"tutorialImage": "https://drive.google.com/uc?export=view&id=1McSxkqPb7ZnlsDKfZfTj6OTS5GvXXKFE",
"taskItems": [],
"_id": "4",
"status": 1,
"createdBy": "Nuwan Gamage",
"createdAt": new Date("2023-08-30T12:00:00Z"),
"tutorialMark": 0,
}
]
},
{
"curriculumCode": "02",
"curriculumLevel": 2,
"curriculumTitle": "Intermediate Sign Language Skills",
"curriculumDescription": "This curriculum focuses on building intermediate sign language skills, allowing learners to engage in more complex conversations and interactions using sign language.",
"curriculumImage": "https://drive.google.com/uc?export=view&id=1b5C6VNO55k3Wj6DJ-vJiUFFEuQnXs5Jo",
"tutorials": [],
"_id": "2",
"status": 1,
"createdBy": "Nuwan Gamage",
"createdAt": new Date("2023-08-30T12:00:00Z"),
"curriculumMark": 0,
},
{
"curriculumCode": "03",
"curriculumLevel": 3,
"curriculumTitle": "Advance Sign Language Skills",
"curriculumDescription": "This curriculum is designed for those who already have a solid foundation in sign language. It covers advanced topics, nuances, and cultural aspects of sign language communication.",
"curriculumImage": "https://drive.google.com/uc?export=view&id=1k-CtiCkM3efhmTBet-JZt2izxVrAdBL3",
"tutorials": [],
"_id": "3",
"status": 1,
"createdBy": "Nuwan Gamage",
"createdAt": new Date("2023-08-30T12:00:00Z"),
"curriculumMark": 100,
}
]
export interface StatusType {
id: number
code: string
description: string
}
// ==============================|| DATA - Status ||============================== //
const status: readonly StatusType[] = [
{ id: 1, code: "Active", description: "Active" },
{ id: 2, code: "New", description: "New" },
{ id: 3, code: "Pending", description: "Pending" },
{ id: 4, code: "Hold", description: "Hold" },
{ id: 5, code: "Rejected", description: "Rejected" },
];
export default status;
import { taskItemType } from "types/taskItem";
export const taskItems: taskItemType[] = [
{
"_id": "1",
"title": "Learn Number One",
"description": "In this lesson, you will learn how to sign the number one. Understanding this basic sign is crucial for counting and expressing singular items or concepts in sign language. Practice the hand shape, movement, and facial expression associated with the sign to improve your fluency.",
"howToDo": [
"- Extend your index finger straight up.",
"- Keep the rest of your fingers closed.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=17sHGfW9zip8xAwbRtUihzxkseKq-Qn7q",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "2",
"title": "Learn Number Two",
"description": "In this lesson, you will learn how to sign the number two. Mastering this sign is essential for counting and expressing pairs or dual concepts in sign language. Pay attention to the hand shape, movement, and facial expression involved in signing the number two to enhance your signing skills.",
"howToDo": [
"- Extend your index and middle fingers straight up.",
"- Keep the rest of your fingers closed.",
"- Hold your hand in front of your chest."],
"referenceImage": "https://drive.google.com/uc?export=view&id=1Nm-we15Rrr04ovRC1sr-ZVMNHbnALRm2",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "3",
"title": "Learn Number Three",
"description": "In this lesson, you will learn how to sign the number three in sign language. Mastering this sign is essential for expressing the quantity or position of three items. Pay attention to the hand shape, movement, and facial expression involved in signing the number three to enhance your signing skills.",
"howToDo": [
"- Extend your index, middle, and ring fingers straight up.",
"- Keep the rest of your fingers closed.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1mKY8D1utbqm8flnNM7DZRKtuPQDXqFSm",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "4",
"title": "Learn Number Four",
"description": "In this lesson, you will learn how to sign the number four in sign language. This sign is commonly used for counting or indicating the quantity or position of four items. Focus on the hand shape, movement, and expression to accurately convey the number four in sign language.",
"howToDo": [
"- Extend your thumb, index, middle, and ring fingers straight up.",
"- Keep your pinky finger folded.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1VtbHehsxbOC04fVR6_Ca6HDv6csH17SJ",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "5",
"title": "Learn Number Five",
"description": "In this lesson, you will learn how to sign the number five in sign language. Mastering this sign is crucial for counting, expressing quantities, or representing the concept of five. Practice the hand shape, movement, and facial expression to effectively communicate the number five in sign language.",
"howToDo": [
"- Extend all your fingers straight up.",
"- Keep your thumb resting on the side of your palm.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1G6qx4dhHTKUPvNag0R3qlDJugNOgcTqM",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "6",
"title": "Learn Number Six",
"description": "In this lesson, you will learn how to sign the number six in sign language. This sign is commonly used for counting, indicating quantities, or representing the concept of six. Pay attention to the hand shape, movement, and expression involved in signing the number six to enhance your signing proficiency.",
"howToDo": [
"- Extend your thumb and pinky finger straight up.",
"- Keep the rest of your fingers closed.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1Q2TbcPTx8KuLp4v2mPL_7GHO0l52DZXw",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "7",
"title": "Learn Number Seven",
"description": "In this lesson, you will learn how to sign the number seven in sign language. Mastering this sign is essential for counting, expressing quantities, or representing the concept of seven. Focus on the hand shape, movement, and facial expression to accurately convey the number seven in sign language.",
"howToDo": [
"- Extend your index, middle, and ring fingers straight up.",
"- Keep your thumb, pinky, and pinky finger folded.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1Jwt1TqXO1L7t3JFqQYzKL4S4GCuqULJ1",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "8",
"title": "Learn Number Eight",
"description": "In this lesson, you will learn how to sign the number eight in sign language. This sign is commonly used for counting, indicating quantities, or representing the concept of eight. Practice the hand shape, movement, and expression to effectively communicate the number eight in sign language.",
"howToDo": [
"- Extend all your fingers straight up.",
"- Cross your index and middle fingers over your ring and pinky fingers.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1zSf4hmqIUCcfebgoD6DTWmJ3NxY36LJL",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "9",
"title": "Learn Number Nine",
"description": "In this lesson, you will learn how to sign the number nine in sign language. Mastering this sign is crucial for counting, expressing quantities, or representing the concept of nine. Pay attention to the hand shape, movement, and expression involved in signing the number nine to enhance your signing proficiency.",
"howToDo": [
"- Extend your thumb and all your fingers straight up.",
"- Keep your pinky finger folded.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=16WB1Ifhq_ozKOO-ehfIqVRB6oC3B5STw",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "10",
"title": "Learn Number Ten",
"description": "In this lesson, you will learn how to sign the number ten in sign language. This sign is commonly used for counting, indicating quantities, or representing the concept of ten. Focus on the hand shape, movement, and facial expression to accurately convey the number ten in sign language.",
"howToDo": [
"- Extend your thumb, index, and middle fingers straight up.",
"- Keep the rest of your fingers closed.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=11tCYFYjdVrr5LIFGyzOrIUg8bTURATZh",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
}
]
\ No newline at end of file
import { Tutorial, tutorialType } from "types/tutorial";
export const tutorials: tutorialType[] = [
{
"tutorialCode": "01",
"tutorialTitle": "Numbers and Counting in Sign Language",
"tutorialDescription": "In this tutorial, you'll discover how to express numbers visually using simple hand gestures. Each number has a unique sign that involves specific finger placements and hand movements. We'll break down each number step by step, providing you with clear instructions, images, and videos to help you learn effectively.",
"tutorialImage": "https://drive.google.com/uc?export=view&id=1GeFzoy3xt8UnfCQE3IPVjPXoAg7GAWgf",
"_id": "1",
"status": 1,
"createdBy": "Nuwan Gamage",
"createdAt": new Date("2023-08-30T12:00:00Z"),
"tutorialMark": 100,
"taskItems": [
{
"_id": "1",
"title": "Learn Number One",
"description": "In this lesson, you will learn how to sign the number one. Understanding this basic sign is crucial for counting and expressing singular items or concepts in sign language. Practice the hand shape, movement, and facial expression associated with the sign to improve your fluency.",
"howToDo": [
"- Extend your index finger straight up.",
"- Keep the rest of your fingers closed.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=17sHGfW9zip8xAwbRtUihzxkseKq-Qn7q",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "2",
"title": "Learn Number Two",
"description": "In this lesson, you will learn how to sign the number two. Mastering this sign is essential for counting and expressing pairs or dual concepts in sign language. Pay attention to the hand shape, movement, and facial expression involved in signing the number two to enhance your signing skills.",
"howToDo": [
"- Extend your index and middle fingers straight up.",
"- Keep the rest of your fingers closed.",
"- Hold your hand in front of your chest."],
"referenceImage": "https://drive.google.com/uc?export=view&id=1Nm-we15Rrr04ovRC1sr-ZVMNHbnALRm2",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "3",
"title": "Learn Number Three",
"description": "In this lesson, you will learn how to sign the number three in sign language. Mastering this sign is essential for expressing the quantity or position of three items. Pay attention to the hand shape, movement, and facial expression involved in signing the number three to enhance your signing skills.",
"howToDo": [
"- Extend your index, middle, and ring fingers straight up.",
"- Keep the rest of your fingers closed.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1mKY8D1utbqm8flnNM7DZRKtuPQDXqFSm",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "4",
"title": "Learn Number Four",
"description": "In this lesson, you will learn how to sign the number four in sign language. This sign is commonly used for counting or indicating the quantity or position of four items. Focus on the hand shape, movement, and expression to accurately convey the number four in sign language.",
"howToDo": [
"- Extend your thumb, index, middle, and ring fingers straight up.",
"- Keep your pinky finger folded.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1VtbHehsxbOC04fVR6_Ca6HDv6csH17SJ",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "5",
"title": "Learn Number Five",
"description": "In this lesson, you will learn how to sign the number five in sign language. Mastering this sign is crucial for counting, expressing quantities, or representing the concept of five. Practice the hand shape, movement, and facial expression to effectively communicate the number five in sign language.",
"howToDo": [
"- Extend all your fingers straight up.",
"- Keep your thumb resting on the side of your palm.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1G6qx4dhHTKUPvNag0R3qlDJugNOgcTqM",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "6",
"title": "Learn Number Six",
"description": "In this lesson, you will learn how to sign the number six in sign language. This sign is commonly used for counting, indicating quantities, or representing the concept of six. Pay attention to the hand shape, movement, and expression involved in signing the number six to enhance your signing proficiency.",
"howToDo": [
"- Extend your thumb and pinky finger straight up.",
"- Keep the rest of your fingers closed.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1Q2TbcPTx8KuLp4v2mPL_7GHO0l52DZXw",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "7",
"title": "Learn Number Seven",
"description": "In this lesson, you will learn how to sign the number seven in sign language. Mastering this sign is essential for counting, expressing quantities, or representing the concept of seven. Focus on the hand shape, movement, and facial expression to accurately convey the number seven in sign language.",
"howToDo": [
"- Extend your index, middle, and ring fingers straight up.",
"- Keep your thumb, pinky, and pinky finger folded.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1Jwt1TqXO1L7t3JFqQYzKL4S4GCuqULJ1",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "8",
"title": "Learn Number Eight",
"description": "In this lesson, you will learn how to sign the number eight in sign language. This sign is commonly used for counting, indicating quantities, or representing the concept of eight. Practice the hand shape, movement, and expression to effectively communicate the number eight in sign language.",
"howToDo": [
"- Extend all your fingers straight up.",
"- Cross your index and middle fingers over your ring and pinky fingers.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1zSf4hmqIUCcfebgoD6DTWmJ3NxY36LJL",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "9",
"title": "Learn Number Nine",
"description": "In this lesson, you will learn how to sign the number nine in sign language. Mastering this sign is crucial for counting, expressing quantities, or representing the concept of nine. Pay attention to the hand shape, movement, and expression involved in signing the number nine to enhance your signing proficiency.",
"howToDo": [
"- Extend your thumb and all your fingers straight up.",
"- Keep your pinky finger folded.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=16WB1Ifhq_ozKOO-ehfIqVRB6oC3B5STw",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
},
{
"_id": "10",
"title": "Learn Number Ten",
"description": "In this lesson, you will learn how to sign the number ten in sign language. This sign is commonly used for counting, indicating quantities, or representing the concept of ten. Focus on the hand shape, movement, and facial expression to accurately convey the number ten in sign language.",
"howToDo": [
"- Extend your thumb, index, and middle fingers straight up.",
"- Keep the rest of your fingers closed.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=11tCYFYjdVrr5LIFGyzOrIUg8bTURATZh",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10
}
]
},
{
"tutorialCode": "02",
"tutorialTitle": "Everyday Vocabulary in Sign Language",
"tutorialDescription": "Teach signs for everyday objects and activities, such as eat, drink, sleep, book, pen, etc.\nIntroduce signs for common words used in daily life.\nProvide visual demonstrations and interactive exercises for learners to practice using these signs.",
"tutorialImage": "https://drive.google.com/uc?export=view&id=1QqmeBBiAojz7jaHUUdQGLyqUVR-mKSsy",
"taskItems": [],
"_id": "2",
"status": 1,
"createdBy": "Nuwan Gamage",
"createdAt": new Date("2023-08-30T12:00:00Z"),
"tutorialMark": 0,
},
{
"tutorialCode": "03",
"tutorialTitle": "Family Signs in Sign Language",
"tutorialDescription": "Teach signs for family members, such as mother, father, sister, brother, etc.\nIntroduce signs for common family-related words, such as family, love, and home.\nProvide visual demonstrations and practice exercises for learners to practice these family signs.",
"tutorialImage": "https://drive.google.com/uc?export=view&id=1_b3-0HAAWu5Ze20IAAKWxUSUS-fac6Dg",
"taskItems": [],
"_id": "3",
"status": 1,
"createdBy": "Nuwan Gamage",
"createdAt": new Date("2023-08-30T12:00:00Z"),
"tutorialMark": 0,
},
{
"tutorialCode": "04",
"tutorialTitle": "Basic Conversational Phrases in Sign Language",
"tutorialDescription": "Teach simple conversational phrases, such as \"What is your name?\" or \"How are you?\"\nIntroduce signs for common question words and phrases.\nProvide visual demonstrations and practice exercises for learners to practice these conversational phrases.",
"tutorialImage": "https://drive.google.com/uc?export=view&id=1McSxkqPb7ZnlsDKfZfTj6OTS5GvXXKFE",
"taskItems": [],
"_id": "4",
"status": 1,
"createdBy": "Nuwan Gamage",
"createdAt": new Date("2023-08-30T12:00:00Z"),
"tutorialMark": 0,
}
]
export const tutorialReqOb: Tutorial = {
"tutorialCode": "01-SAMPLE",
"tutorialTitle": "Numbers and Counting in Sign Language",
"tutorialImage": "https://drive.google.com/uc?export=view&id=1GeFzoy3xt8UnfCQE3IPVjPXoAg7GAWgf",
"tutorialDescription": "In this tutorial, you'll discover how to express numbers visually using simple hand gestures. Each number has a unique sign that involves specific finger placements and hand movements. We'll break down each number step by step, providing you with clear instructions, images, and videos to help you learn effectively.",
"taskItems": [
{
"title": "Learn Number One",
"description": "In this lesson, you will learn how to sign the number one. Understanding this basic sign is crucial for counting and expressing singular items or concepts in sign language. Practice the hand shape, movement, and facial expression associated with the sign to improve your fluency.",
"howToDo": [
"- Extend your index finger straight up.",
"- Keep the rest of your fingers closed.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=17sHGfW9zip8xAwbRtUihzxkseKq-Qn7q",
"referenceVideo": "",
"taskItemMark": 10
}
],
"createdBy": "Nuwan Gamage"
}
\ No newline at end of file
import { userProgressType } from "types/userProgress";
export const userProgress: userProgressType = {
"_id": "64f1f99f9a5bff5bfdb1bbc9",
"userId": "64f087fda8db35e2f70753ab",
"curriculums": [
{
"curriculumCode": "01",
"curriculumLevel": 1,
"curriculumTitle": "Basic Sign Language Skills",
"curriculumDescription": "This curriculum teaches basic sign language skills to help beginners communicate effectively using sign language.",
"curriculumImage": "https://drive.google.com/uc?export=view&id=1YACBlu7X-O7-DKv5DoW3AM9kgfT7Yhdc",
"curriculumMark": 100,
"curriculumMarkUser": 24,
"curriculumSpentTime": 15,
"tutorials": [
{
"tutorialCode": "01",
"tutorialTitle": "Numbers and Counting",
"tutorialDescription": "In this tutorial, you'll discover how to express numbers visually using simple hand gestures. Each number has a unique sign that involves specific finger placements and hand movements. We'll break down each number step by step, providing you with clear instructions, images, and videos to help you learn effectively.",
"tutorialImage": "https://drive.google.com/uc?export=view&id=1GeFzoy3xt8UnfCQE3IPVjPXoAg7GAWgf",
"tutorialMarks": 100,
"tutorialMarkUser": 24,
"tutorialSpentTime": 15,
"taskItems": [
{
"title": "Learn Number One",
"description": "In this lesson, you will learn how to sign the number one. Understanding this basic sign is crucial for counting and expressing singular items or concepts in sign language. Practice the hand shape, movement, and facial expression associated with the sign to improve your fluency.",
"howToDo": [
"- Extend your index finger straight up.",
"- Keep the rest of your fingers closed.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=17sHGfW9zip8xAwbRtUihzxkseKq-Qn7q",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10,
"taskItemMarkUser": 8,
"taskItemSpentTime": 5
},
{
"title": "Learn Number Two",
"description": "In this lesson, you will learn how to sign the number two. Mastering this sign is essential for counting and expressing pairs or dual concepts in sign language. Pay attention to the hand shape, movement, and facial expression involved in signing the number two to enhance your signing skills.",
"howToDo": [
"- Extend your index and middle fingers straight up.",
"- Keep the rest of your fingers closed.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1Nm-we15Rrr04ovRC1sr-ZVMNHbnALRm2",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10,
"taskItemMarkUser": 8,
"taskItemSpentTime": 5
},
{
"title": "Learn Number Three",
"description": "In this lesson, you will learn how to sign the number three in sign language. Mastering this sign is essential for expressing the quantity or position of three items. Pay attention to the hand shape, movement, and facial expression involved in signing the number three to enhance your signing skills.",
"howToDo": [
"- Extend your index, middle, and ring fingers straight up.",
"- Keep the rest of your fingers closed.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1mKY8D1utbqm8flnNM7DZRKtuPQDXqFSm",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10,
"taskItemMarkUser": 8,
"taskItemSpentTime": 5
},
{
"title": "Learn Number Four",
"description": "In this lesson, you will learn how to sign the number four in sign language. This sign is commonly used for counting or indicating the quantity or position of four items. Focus on the hand shape, movement, and expression to accurately convey the number four in sign language.",
"howToDo": [
"- Extend your thumb, index, middle, and ring fingers straight up.",
"- Keep your pinky finger folded.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1VtbHehsxbOC04fVR6_Ca6HDv6csH17SJ",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10,
"taskItemMarkUser": 0,
"taskItemSpentTime": 0
},
{
"title": "Learn Number Five",
"description": "In this lesson, you will learn how to sign the number five in sign language. Mastering this sign is crucial for counting, expressing quantities, or representing the concept of five. Practice the hand shape, movement, and facial expression to effectively communicate the number five in sign language.",
"howToDo": [
"- Extend all your fingers straight up.",
"- Keep your thumb resting on the side of your palm.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1G6qx4dhHTKUPvNag0R3qlDJugNOgcTqM",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10,
"taskItemMarkUser": 0,
"taskItemSpentTime": 0
},
{
"title": "Learn Number Six",
"description": "In this lesson, you will learn how to sign the number six in sign language. This sign is commonly used for counting, indicating quantities, or representing the concept of six. Pay attention to the hand shape, movement, and expression involved in signing the number six to enhance your signing proficiency.",
"howToDo": [
"- Extend your thumb and pinky finger straight up.",
"- Keep the rest of your fingers closed.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1Q2TbcPTx8KuLp4v2mPL_7GHO0l52DZXw",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10,
"taskItemMarkUser": 0,
"taskItemSpentTime": 0
},
{
"title": "Learn Number Seven",
"description": "In this lesson, you will learn how to sign the number seven in sign language. Mastering this sign is essential for counting, expressing quantities, or representing the concept of seven. Focus on the hand shape, movement, and facial expression to accurately convey the number seven in sign language.",
"howToDo": [
"- Extend your index, middle, and ring fingers straight up.",
"- Keep your thumb, pinky, and pinky finger folded.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1Jwt1TqXO1L7t3JFqQYzKL4S4GCuqULJ1",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10,
"taskItemMarkUser": 0,
"taskItemSpentTime": 0
},
{
"title": "Learn Number Eight",
"description": "In this lesson, you will learn how to sign the number eight in sign language. This sign is commonly used for counting, indicating quantities, or representing the concept of eight. Practice the hand shape, movement, and expression to effectively communicate the number eight in sign language.",
"howToDo": [
"- Extend all your fingers straight up.",
"- Cross your index and middle fingers over your ring and pinky fingers.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=1zSf4hmqIUCcfebgoD6DTWmJ3NxY36LJL",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10,
"taskItemMarkUser": 0,
"taskItemSpentTime": 0
},
{
"title": "Learn Number Nine",
"description": "In this lesson, you will learn how to sign the number nine in sign language. Mastering this sign is crucial for counting, expressing quantities, or representing the concept of nine. Pay attention to the hand shape, movement, and expression involved in signing the number nine to enhance your signing proficiency.",
"howToDo": [
"- Extend your thumb and all your fingers straight up.",
"- Keep your pinky finger folded.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=16WB1Ifhq_ozKOO-ehfIqVRB6oC3B5STw",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10,
"taskItemMarkUser": 0,
"taskItemSpentTime": 0
},
{
"title": "Learn Number Ten",
"description": "In this lesson, you will learn how to sign the number ten in sign language. This sign is commonly used for counting, indicating quantities, or representing the concept of ten. Focus on the hand shape, movement, and facial expression to accurately convey the number ten in sign language.",
"howToDo": [
"- Extend your thumb, index, and middle fingers straight up.",
"- Keep the rest of your fingers closed.",
"- Hold your hand in front of your chest."
],
"referenceImage": "https://drive.google.com/uc?export=view&id=11tCYFYjdVrr5LIFGyzOrIUg8bTURATZh",
"referenceVideo": "https://example.com/number_seven_video.mp4",
"taskItemMark": 10,
"taskItemMarkUser": 0,
"taskItemSpentTime": 0
}
]
},
{
"tutorialCode": "02",
"tutorialTitle": "Everyday Vocabulary",
"tutorialDescription": "Teach signs for everyday objects and activities, such as eat, drink, sleep, book, pen, etc.\nIntroduce signs for common words used in daily life.\nProvide visual demonstrations and interactive exercises for learners to practice using these signs.",
"tutorialImage": "https://drive.google.com/uc?export=view&id=1QqmeBBiAojz7jaHUUdQGLyqUVR-mKSsy",
"tutorialMarks": 0,
"tutorialMarkUser": 0,
"tutorialSpentTime": 0,
"taskItems": []
},
{
"tutorialCode": "03",
"tutorialTitle": "Family Signs",
"tutorialDescription": "Teach signs for family members, such as mother, father, sister, brother, etc.\nIntroduce signs for common family-related words, such as family, love, and home.\nProvide visual demonstrations and practice exercises for learners to practice these family signs.",
"tutorialImage": "https://drive.google.com/uc?export=view&id=1_b3-0HAAWu5Ze20IAAKWxUSUS-fac6Dg",
"tutorialMarks": 0,
"tutorialMarkUser": 0,
"tutorialSpentTime": 0,
"taskItems": []
},
{
"tutorialCode": "04",
"tutorialTitle": "Basic Conversational Phrases",
"tutorialDescription": "Teach simple conversational phrases, such as \"What is your name?\" or \"How are you?\"\nIntroduce signs for common question words and phrases.\nProvide visual demonstrations and practice exercises for learners to practice these conversational phrases.",
"tutorialImage": "https://drive.google.com/uc?export=view&id=1McSxkqPb7ZnlsDKfZfTj6OTS5GvXXKFE",
"tutorialMarks": 0,
"tutorialMarkUser": 0,
"tutorialSpentTime": 0,
"taskItems": []
}
]
},
],
"totalCurriculumsMarks": 24,
"totalCurriculumSpentTime": 15,
"status": 1,
"createdAt": new Date("2023-08-30T12:00:00Z"),
"updatedAt": new Date("2023-08-30T12:00:00Z")
}
\ No newline at end of file
......@@ -10,7 +10,7 @@ import Message from './Message';
import Notification from './Notification';
import Profile from './Profile';
import Search from './Search';
// import Customization from './Customization';
import Customization from './Customization';
import MobileSection from './MobileSection';
// import MegaMenuSection from './MegaMenuSection';
......@@ -42,7 +42,7 @@ const HeaderContent = () => {
<Notification />
<Message />
{/* <Customization /> */}
<Customization />
{!downLG && <Profile />}
{downLG && <MobileSection />}
</>
......
......@@ -3,19 +3,20 @@ import { FormattedMessage } from 'react-intl';
// assets
import {
AudioOutlined,
BookOutlined,
CarryOutOutlined,
CreditCardOutlined,
FastForwardOutlined,
FileTextOutlined,
LoginOutlined,
RedditOutlined,
RocketOutlined,
ShoppingOutlined,
TeamOutlined,
TranslationOutlined,
UserOutlined,
AudioOutlined,
VideoCameraOutlined,
RedditOutlined,
} from '@ant-design/icons';
// type
......@@ -33,9 +34,10 @@ const icons = {
RocketOutlined,
BookOutlined,
TranslationOutlined,
AudioOutlined,
VideoCameraOutlined,
FastForwardOutlined,
RedditOutlined,
AudioOutlined,
VideoCameraOutlined
};
// ==============================|| MENU ITEMS - SUPPORT ||============================== //
......@@ -80,13 +82,27 @@ const application: NavItemType = {
icon: icons.TranslationOutlined,
url: '/ssl-translate/process',
},
{
id: 'video-to-sign-language',
title: <FormattedMessage id="video-to-sign-language" />,
type: 'collapse',
icon: icons.RedditOutlined,
children: [
{
id: 'video-translate',
title: <FormattedMessage id="video-translate" />,
type: 'item',
url: '/video-to-sign-language/VideoTranslate',
}
]
},
{
id: 'emotion-detection',
title: <FormattedMessage id="emotion-detection" />,
type: 'collapse',
icon: icons.RedditOutlined,
children: [
{
id: 'audio-detection',
title: <FormattedMessage id="audio-detection" />,
......@@ -101,7 +117,7 @@ const application: NavItemType = {
icon: icons.VideoCameraOutlined,
url: '/emotion-detection/video-detection',
},
]
},
......@@ -137,6 +153,12 @@ const application: NavItemType = {
type: 'item',
url: '/learning-management/curriculums-subscribed',
},
{
id: 'learning-curriculums-subscribed-tutorial',
title: <FormattedMessage id="learning-curriculums-subscribed-tutorial" />,
type: 'item',
url: '/learning-management/curriculums-subscribed-tutorial/0/0',
},
{
id: 'learning-lead-board',
title: <FormattedMessage id="learning-lead-board" />,
......
// material-ui
// project import
// ==============================|| Dashboard ||============================== //
const Dashboard = () => (
<>
</>
);
const Dashboard = () => {
return (
<>
</>
);
}
export default Dashboard;
import { useState } from 'react';
// material-ui
import { Grid } from '@mui/material';
import { ArrowDownOutlined, ArrowUpOutlined } from '@ant-design/icons';
import { Card, CardContent, CardHeader, Collapse, Grid, IconButton, Typography } from '@mui/material';
// third-party
......@@ -15,11 +17,67 @@ import WelcomeBanner from 'sections/learning-management/WelcomeBanner';
// ==============================|| Dashboard ||============================== //
const Dashboard = () => {
const [expanded, setExpanded] = useState(false);
const handleExpandClick = () => {
setExpanded(!expanded);
};
return (
<Grid container rowSpacing={4.5} columnSpacing={3}>
<Grid container rowSpacing={2} columnSpacing={3}>
<Grid item xs={12}>
<WelcomeBanner />
</Grid>
</Grid>
<Grid md={12} item>
<Card>
<CardHeader
title="Learn Sign Language Curriculums"
subheader="Building a Solid Foundation, Enhancing Skills, and Mastering Sign Language"
action={
<IconButton
aria-expanded={expanded}
aria-label="show more"
onClick={handleExpandClick}
>
{expanded ? <ArrowUpOutlined /> : <ArrowDownOutlined />}
</IconButton>
}
/>
<Collapse in={expanded} timeout="auto" unmountOnExit>
<CardContent>
<Typography variant="body1">
Explore the world of sign language with our comprehensive Curriculum offerings. We provide three levels of sign language Curriculums designed to cater to learners of varying proficiency levels.
</Typography>
<Typography variant="subtitle1" sx={{ mt: 3 }}>
1. Base Level - Learn Sign Language:
</Typography>
<Typography variant="body2" sx={{ mt: 3 }}>
Start your sign language journey with our Base Level Curriculum. Perfect for beginners, this Curriculum offers essential sign language skills, including basic vocabulary, grammar, and sentence structures. Through interactive lessons and hands-on practice, you'll learn to express yourself through gestures and facial expressions, enabling effective communication with the deaf and hard of hearing community. Join us today and begin breaking down communication barriers.
</Typography>
<Typography variant="subtitle1" sx={{ mt: 3 }}>
2. Medium Level - Enhancing Sign Language Skills:
</Typography>
<Typography variant="body2" sx={{ mt: 3 }}>
Take your sign language skills to the next level with our Medium Level Curriculum. Designed for learners with some basic knowledge of sign language, this Curriculum focuses on expanding vocabulary, improving grammar usage, and honing expressive abilities. Engage in challenging yet rewarding lessons, interactive exercises, role-playing scenarios, and conversations to strengthen fluency. By delving deeper into sign language communication, including idiomatic expressions and storytelling techniques, you'll be able to connect with the deaf community on a deeper level.
</Typography>
<Typography variant="subtitle1" sx={{ mt: 3 }}>
3. Advanced Level - Mastering Sign Language:
</Typography>
<Typography variant="body2" sx={{ mt: 3 }}>
Become a proficient sign language user with our Advanced Level Curriculum. This comprehensive Curriculum is designed for experienced sign language learners seeking mastery. Dive into advanced topics such as complex grammar structures, specialized vocabulary, and cultural nuances. Through immersive activities, real-life simulations, and in-depth discussions, you'll refine your receptive and expressive skills. Explore various sign language modalities and gain a deep understanding of different signing systems. With personalized feedback and guidance from expert instructors, our Advanced Level Curriculum equips you to engage confidently in diverse sign language contexts.
</Typography>
<Typography variant="subtitle2" sx={{ mt: 3 }}>
Whether you're starting from scratch or aiming to enhance your existing sign language skills, our Curriculums offer a supportive learning environment and the opportunity to build connections with the deaf community. Join us today and embark on your sign language journey!
</Typography>
</CardContent>
</Collapse>
</Card>
</Grid>
</Grid>
)
}
......
import { useEffect, useState } from 'react';
// material-ui
// third-party
import {
Accordion, AccordionDetails, AccordionSummary,
Box,
FormControl,
Grid,
MenuItem,
Pagination,
Select,
SelectChangeEvent,
Slide,
Stack,
Theme,
Typography,
useMediaQuery
} from '@mui/material';
import { useTheme } from '@mui/material/styles';
// project import
import MainCard from 'components/MainCard';
import ScrollX from 'components/ScrollX';
import usePagination from 'hooks/usePagination';
import { GlobalFilter } from 'utils/react-table';
// types
// assets
//types
import { BookOutlined } from '@ant-design/icons';
import useAuth from 'hooks/useAuth';
import CurriculumSection from 'sections/learning-management/learning-curriculums-subscribed/CurriculumSection';
import EmptyCurriculumCard from 'sections/learning-management/learning-curriculums-subscribed/skeleton/EmptyCurriculumCard';
import { useDispatch, useSelector } from 'store';
import { openSnackbar } from 'store/reducers/snackbar';
import { fetchUserProgress, toInitialState } from 'store/reducers/userProgress';
import { curriculumTypeUserProgress, userProgressType } from 'types/userProgress';
// ==============================|| List ||============================== //
const allColumns = [
{
id: 1,
header: 'Default'
},
{
id: 2,
header: 'Title'
}
];
const List = () => {
const theme = useTheme();
const dispatch = useDispatch();
const { userProgress, error, success, isLoading } = useSelector(state => state.userProgress);
const { user } = useAuth();
const [data, setData] = useState<userProgressType["curriculums"]>([])
const matchDownSM = useMediaQuery((theme: Theme) => theme.breakpoints.down('sm'));
const [curriculumsData, setCurriculumsData] = useState<userProgressType["curriculums"]>([]);
const [sortBy, setSortBy] = useState('Default');
const [globalFilter, setGlobalFilter] = useState('');
const [page, setPage] = useState(1);
const handleChange = (event: SelectChangeEvent) => {
setSortBy(event.target.value as string);
};
// search
useEffect(() => {
if (!data || data.length == 0) return
const newData = data?.filter((value: any) => {
if (globalFilter) {
return value.curriculumTitle.toLowerCase().includes(globalFilter.toLowerCase());
} else {
return value;
}
});
setCurriculumsData(newData);
}, [globalFilter, data]);
const PER_PAGE = 6;
const count = Math.ceil(curriculumsData?.length! / PER_PAGE);
const _DATA = usePagination(curriculumsData, PER_PAGE);
const handleChangePage = (e: any, p: any) => {
setPage(p);
_DATA.jump(p);
};
const [expanded, setExpanded] = useState<string | false>('panel0');
const handleAccordionChange = (panel: string) => (event: React.SyntheticEvent, newExpanded: boolean) => {
setExpanded(newExpanded ? panel : false);
};
/**
* API Config
* User Progress API
*/
useEffect(() => {
if (!user) {
// User is missing
dispatch(
openSnackbar({
open: true,
message: 'User data is missing',
variant: 'alert',
alert: {
color: 'warning',
},
close: true,
})
);
} else if (!user.id) {
// User ID is missing
dispatch(
openSnackbar({
open: true,
message: 'User ID is missing',
variant: 'alert',
alert: {
color: 'warning',
},
close: true,
})
);
} else {
dispatch(fetchUserProgress(user.id));
}
}, [dispatch]);
useEffect(() => {
if (userProgress) {
setData(userProgress?.curriculums);
}
}, [userProgress])
// handel error
useEffect(() => {
if (error != null) {
dispatch(
openSnackbar({
open: true,
//@ts-ignore
message: error ? error.Message : "Something went wrong ...",
variant: 'alert',
alert: {
color: 'error'
},
close: true
})
);
dispatch(toInitialState())
}
}, [error])
// handel success
useEffect(() => {
if (success != null) {
dispatch(
openSnackbar({
open: true,
message: success,
variant: 'alert',
alert: {
color: 'success'
},
close: true
})
);
dispatch(toInitialState())
}
}, [success])
if (isLoading) {
return <div>Loading...</div>;
}
return (
<MainCard content={false}>
<ScrollX>
</ScrollX>
</MainCard>
<>
<Box sx={{ position: 'relative', marginBottom: 3 }}>
<Stack direction="row" alignItems="center">
<Stack
direction={matchDownSM ? 'column' : 'row'}
sx={{ width: '100%' }}
spacing={1}
justifyContent="space-between"
alignItems="center"
>
{/* @ts-ignore */}
<GlobalFilter preGlobalFilteredRows={data} globalFilter={globalFilter} setGlobalFilter={setGlobalFilter} />
<Stack direction={matchDownSM ? 'column' : 'row'} alignItems="center" spacing={1}>
<FormControl sx={{ m: 1, minWidth: 120 }}>
<Select
value={sortBy}
onChange={handleChange}
displayEmpty
inputProps={{ 'aria-label': 'Without label' }}
renderValue={(selected) => {
if (!selected) {
return <Typography variant="subtitle1">Sort By</Typography>;
}
return <Typography variant="subtitle2">Sort by ({sortBy})</Typography>;
}}
>
{allColumns.map((column) => {
return (
<MenuItem key={column.id} value={column.header}>
{column.header}
</MenuItem>
);
})}
</Select>
</FormControl>
</Stack>
</Stack>
</Stack>
</Box>
<Grid container spacing={3}>
{curriculumsData?.length! > 0 ? (
_DATA
.currentData()
.sort(function (a: any, b: any) {
if (sortBy === 'Title') return a.curriculumTitle.localeCompare(b.curriculumTitle);
return a;
})
.map((curriculum: curriculumTypeUserProgress, index: number) => (
<Slide key={index} direction="up" in={true} timeout={50}>
<Grid item xs={12} sm={12} lg={12}>
<Box
sx={{
'& .MuiAccordion-root': {
borderColor: theme.palette.divider,
'& .MuiAccordionSummary-root': {
bgcolor: 'transparent',
flexDirection: 'row',
'&:focus-visible': {
bgcolor: 'primary.lighter'
}
},
'& .MuiAccordionDetails-root': {
borderColor: theme.palette.divider
},
'& .Mui-expanded': {
color: theme.palette.primary.main
}
}
}}
>
<Accordion expanded={expanded === `panel${index}`} onChange={handleAccordionChange(`panel${index}`)}>
<AccordionSummary aria-controls={`panel${index}d-content`} id={`panel${index}d-header`}>
<Stack direction="row" spacing={1.5} alignItems="center">
<BookOutlined style={{ fontSize: '26px' }} />
<Typography variant="h4">{curriculum.curriculumTitle}</Typography>
</Stack>
</AccordionSummary>
<AccordionDetails>
<CurriculumSection curriculum={curriculum} curriculumIndex={index} />
</AccordionDetails>
</Accordion>
</Box>
</Grid>
</Slide>
))
) : (
<EmptyCurriculumCard title={'System have no curriculumsData yet.'} />
)}
</Grid>
<Stack spacing={2} sx={{ p: 2.5 }} alignItems="flex-end">
<Pagination
count={count}
size="medium"
page={page}
showFirstButton
showLastButton
variant="combined"
color="primary"
onChange={handleChangePage}
/>
</Stack>
</>
)
}
......
import { useEffect, useRef, useState } from "react";
// material-ui
import { Alert, Box, Button, ButtonGroup, Dialog, DialogActions, DialogContent, DialogTitle, Divider, Grid, LinearProgress, List, ListItemAvatar, ListItemButton, ListItemSecondaryAction, ListItemText, Stack, Typography } from "@mui/material";
import { useTheme } from '@mui/material/styles';
// project import
import AntAvatar from 'components/@extended/Avatar';
import MainCard from "components/MainCard";
import UploadSingleFile from 'components/third-party/dropzone/SingleFile';
// types
import { tutorialTypeUserProgress } from "types/userProgress";
// assets
import { CheckCircleOutlined, CheckOutlined, ClockCircleOutlined, FileMarkdownFilled, LeftOutlined, PlayCircleOutlined, RightOutlined, TrophyOutlined, UserOutlined } from "@ant-design/icons";
import { PopupTransition } from "components/@extended/Transitions";
import ReportCard from "components/cards/statistics/ReportCard";
import { itemResultProps, selectedCommonDataProps, selectedItemContentProps } from "./types/types";
import CircularWithPath from "components/@extended/progress/CircularWithPath";
import useAuth from "hooks/useAuth";
import { useParams } from "react-router";
import Webcam from 'react-webcam';
import { useDispatch, useSelector } from "store";
import { CalculateMarks, toInitialState } from "store/reducers/marksCalculator";
import { openSnackbar } from "store/reducers/snackbar";
import { fetchUserProgress, updateUserProgress, toInitialState as userProgressToInitialState } from "store/reducers/userProgress";
// action style
const actionSX = {
mt: 0.75,
ml: 1,
top: 'auto',
right: 'auto',
alignSelf: 'flex-start',
transform: 'none'
};
function convertMinutesToHMS(minutes: number) {
const hours = Math.floor(minutes / 60);
const remainingMinutes = minutes % 60;
const seconds = Math.round((minutes % 1) * 60);
return `${hours} hr ${remainingMinutes} min ${seconds} sec`;
}
// ==============================|| Tutorial ||============================== //
const Tutorial = () => {
const theme = useTheme();
const dispatch = useDispatch();
const { marksCalculator, error, success, isLoading } = useSelector(state => state.marksCalculator);
const { userProgress, error: errorUserProgress, isLoading: isLoadingUserProgress, success: successUserProgress } = useSelector(state => state.userProgress);
const { curriculumIndex, tutorialIndex } = useParams();
const { user } = useAuth()
const [data, setData] = useState<tutorialTypeUserProgress>()
const [selectedItem, setSelectedItem] = useState<{ selectedCommonData: selectedCommonDataProps | null, backgroundColor: any | null }>({
selectedCommonData: null,
backgroundColor: 'white', // Initial background color
});
const [selectedItemContent, setSelectedItemContent] = useState<selectedItemContentProps | null>(null)
const [desc, setDesc] = useState("")
const [readMore, setReadMore] = useState(false)
const [itemDesc, setItemDesc] = useState("")
const [itemReadMore, setItemReadMore] = useState(false)
const [itemReferenceData, setItemReferenceData] = useState<"image" | "video">("image")
const [itemPracticeReferenceData, setItemPracticeReferenceData] = useState<"capture" | "upload">("capture")
const [itemResult, setItemResult] = useState<itemResultProps>({
itemMarkUser: 0,
status: "Pending"
})
useEffect(() => {
if (!curriculumIndex || !tutorialIndex) return
if (userProgress) {
const selectedTutorial = userProgress?.curriculums?.[parseInt(curriculumIndex)]?.tutorials?.[parseInt(tutorialIndex)];
if (selectedTutorial) {
setData(selectedTutorial);
}
}
}, [userProgress]);
const handleItemClick = (item: selectedCommonDataProps, backgroundColor: any) => {
setSelectedItem({
selectedCommonData: {
userId: item.userId,
curriculumCode: item.curriculumCode,
tutorialCode: item.tutorialCode,
title: item.title
},
backgroundColor,
});
};
useEffect(() => {
setDesc(data?.tutorialDescription?.slice(0, 200)!)
}, [data])
useEffect(() => {
if (!selectedItemContent?.description) return
setItemDesc(selectedItemContent?.description.slice(0, 200)!)
}, [selectedItemContent])
useEffect(() => {
// Filter data based on selectedItem.title
if (selectedItem && data) {
const filteredItem = data?.taskItems!.find((item) => item.title === selectedItem.selectedCommonData?.title);
setSelectedItemContent({
userId: selectedItem.selectedCommonData?.userId!,
curriculumCode: selectedItem.selectedCommonData?.curriculumCode!,
tutorialCode: selectedItem.selectedCommonData?.tutorialCode!,
...filteredItem!
} || null)
} else {
setSelectedItemContent(null);
}
}, [selectedItem]);
useEffect(() => {
setItemResult({
itemMarkUser: ((selectedItemContent?.taskItemMark! * parseInt(marksCalculator?.confidence!)) / 100)!,
status: marksCalculator?.status.toUpperCase()!
})
}, [marksCalculator])
//alert model
const [openModel, setOpenModel] = useState(false);
const [modelOpenFor, setModelOpenFor] = useState<"Practice" | "Complete">("Practice")
const handleModelClose = () => {
setOpenModel(!openModel);
};
const handleModelOpen = (modelFor: "Practice" | "Complete") => {
setModelOpenFor(modelFor)
setOpenModel(true);
};
// web cam
const webcamRef = useRef<Webcam>(null);
const [capturedImage, setCapturedImage] = useState<string | null>(null);
const capture = () => {
if (webcamRef.current) {
const imageSrc = webcamRef.current.getScreenshot();
setCapturedImage(imageSrc);
}
};
// upload image
const [uploadImage, setUploadImage] = useState<any>();
const TaskTrigger = (uploadImage: any, imgData: any, title: string) => {
if (itemPracticeReferenceData == "capture") {
if (typeof imgData === 'string' && imgData.trim() !== '') {
const curriculumIndex: number = 1;
const tutorialIndex: number = 1;
const imageData: any = imgData; // Your image data
let targetClass: string = ""; // Your target class
switch (title) {
case "Learn Number One":
targetClass = "One";
break;
case "Learn Number Two":
targetClass = "Two";
break;
case "Learn Number Three":
targetClass = "Three";
break;
case "Learn Number Four":
targetClass = "Four";
break;
case "Learn Number Five":
targetClass = "Five";
break;
case "Learn Number Six":
targetClass = "Six";
break;
case "Learn Number Seven":
targetClass = "Seven";
break;
case "Learn Number Eight":
targetClass = "Eight";
break;
case "Learn Number Nine":
targetClass = "Nine";
break;
case "Learn Number Ten":
targetClass = "Ten";
break;
default:
targetClass = "Initial";
break;
}
// Assuming imageData is a base64-encoded image
const base64Image = imageData; // Replace with your actual base64 data
const byteCharacters = atob(base64Image.split(',')[1]);
const byteNumbers = new Array(byteCharacters.length);
for (let i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
const blob = new Blob([byteArray], { type: 'image/jpeg' });
const file = new File([blob], 'image.jpg', { type: 'image/jpeg' });
dispatch(CalculateMarks(curriculumIndex, tutorialIndex, file, targetClass));
} else {
dispatch(
openSnackbar({
open: true,
//@ts-ignore
message: "Invalid Image Data",
variant: 'alert',
alert: {
color: 'warning'
},
close: true
})
);
}
} else if (itemPracticeReferenceData == "upload") {
const curriculumIndex: number = 1;
const tutorialIndex: number = 1;
const imageData: any = uploadImage[0]; // Your image data
let targetClass: string = ""; // Your target class
switch (title) {
case "Learn Number One":
targetClass = "One";
break;
case "Learn Number Two":
targetClass = "Two";
break;
case "Learn Number Three":
targetClass = "Three";
break;
case "Learn Number Four":
targetClass = "Four";
break;
case "Learn Number Five":
targetClass = "Five";
break;
case "Learn Number Six":
targetClass = "Six";
break;
case "Learn Number Seven":
targetClass = "Seven";
break;
case "Learn Number Eight":
targetClass = "Eight";
break;
case "Learn Number Nine":
targetClass = "Nine";
break;
case "Learn Number Ten":
targetClass = "Ten";
break;
default:
targetClass = "Initial";
break;
}
dispatch(CalculateMarks(curriculumIndex, tutorialIndex, imageData, targetClass));
}
}
/**
* API Config
* Marks Calculator API
*/
// handel error
useEffect(() => {
if (error != null) {
dispatch(
openSnackbar({
open: true,
//@ts-ignore
message: error ? error.Message : "Something went wrong ...",
variant: 'alert',
alert: {
color: 'error'
},
close: true
})
);
dispatch(toInitialState())
}
}, [error])
// handel success
useEffect(() => {
if (success != null) {
dispatch(
openSnackbar({
open: true,
message: success,
variant: 'alert',
alert: {
color: 'success'
},
close: true
})
);
if (modelOpenFor == "Complete") {
dispatch(updateUserProgress(
user?.id!,
userProgress?.curriculums?.[parseInt(curriculumIndex!)].curriculumCode!,
data?.tutorialCode!,
selectedItemContent?.title!,
((selectedItemContent?.taskItemMark! * parseInt(marksCalculator?.confidence!)) / 100)!,
0
))
}
dispatch(toInitialState())
}
}, [success])
// handel error
useEffect(() => {
if (errorUserProgress != null) {
dispatch(
openSnackbar({
open: true,
//@ts-ignore
message: errorUserProgress ? errorUserProgress.Message : "Something went wrong ...",
variant: 'alert',
alert: {
color: 'error'
},
close: true
})
);
dispatch(userProgressToInitialState())
}
}, [errorUserProgress])
// handel success
useEffect(() => {
if (successUserProgress != null) {
dispatch(
openSnackbar({
open: true,
message: successUserProgress,
variant: 'alert',
alert: {
color: 'success'
},
close: true
})
);
dispatch(fetchUserProgress(user?.id!));
dispatch(userProgressToInitialState())
}
}, [successUserProgress])
return (
<>
<Grid container spacing={2}>
<Grid item md={12}>
<MainCard title="Overview">
<Grid container spacing={2}>
<Grid
item
xs={7}
sm={7}
sx={{}}
>
<Stack>
<Typography variant="h3" sx={{ mb: 2 }}>
{data?.tutorialTitle}
</Typography>
<Typography variant="h5" sx={{ textAlign: "justify" }}>
{desc}
<span style={{ fontWeight: "bold", cursor: "pointer" }}
onClick={() => {
if (!readMore) {
setDesc(data?.tutorialDescription!)
setReadMore(true)
} else {
setDesc(data?.tutorialDescription?.slice(0, 200)!)
setReadMore(false)
}
}} color="secondary">
{readMore ? "Show Less" : "...Read More"}
</span>
</Typography>
<Typography variant="h4" sx={{ pt: 3, pb: 1, zIndex: 1 }}>
{(data?.tutorialMarkUser! / data?.tutorialMarks!) * 100}% Completed
</Typography>
<Box sx={{ maxWidth: '60%' }}>
<LinearProgress variant="determinate" color="success" value={(data?.tutorialMarkUser! / data?.tutorialMarks!) * 100} />
</Box>
</Stack>
</Grid>
<Grid md={5} item>
<Grid container spacing={2}>
<Grid md={12} item>
<ReportCard
primary={`${data?.tutorialMarkUser?.toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}`}
secondary="Tutorial User Mark" color={theme.palette.error.main} iconPrimary={FileMarkdownFilled} />
</Grid>
<Grid md={12} item>
<ReportCard primary={`${convertMinutesToHMS(data?.tutorialSpentTime!)}`} secondary="Tutorial Time Spent On" color={theme.palette.success.dark} iconPrimary={ClockCircleOutlined} />
</Grid>
</Grid>
</Grid>
</Grid>
</MainCard>
</Grid>
<Grid item md={4}>
<MainCard title="Task List">
<List
component="nav"
sx={{
py: 0,
'& .MuiListItemButton-root': {
'& .MuiListItemSecondaryAction-root': { ...actionSX, position: 'relative' }
}
}}
>
{data?.taskItems?.map((item, index) => {
const isSelected = selectedItem.selectedCommonData?.title === item.title;
const backgroundColor = isSelected ? theme.palette.primary.lighter : 'white';
const iconColor = isSelected ? 'warning' : 'success';
return (
<>
<ListItemButton
key={index}
divider
onClick={() => handleItemClick({
userId: "",
curriculumCode: "",
tutorialCode: data.tutorialCode!,
title: item.title
}, backgroundColor)}
sx={{ backgroundColor }}
>
<ListItemAvatar>
<AntAvatar alt="Basic" type="combined" color={iconColor}>
<ClockCircleOutlined />
</AntAvatar>
</ListItemAvatar>
<ListItemText primary={<Typography variant="subtitle1">{item.title}</Typography>} />
<ListItemSecondaryAction>
<Stack alignItems="flex-end">
<Typography variant="subtitle1" noWrap>User Mark : {item.taskItemMarkUser?.toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</Typography>
<Typography variant="h6" color="secondary" noWrap> Mark : {item.taskItemMark?.toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})} </Typography>
</Stack>
</ListItemSecondaryAction>
</ListItemButton>
</>
)
})}
</List>
</MainCard>
</Grid>
<Grid item md={8}>
<MainCard title="Task View">
<Grid container spacing={2}>
{selectedItemContent !== null
?
<>
<Grid item md={8}>
<Typography variant="h4" sx={{ mb: 2 }}>
{selectedItemContent?.title}
</Typography>
<Typography variant="h5" sx={{ textAlign: "justify", mb: 2 }}>
{itemDesc}
<span style={{ fontWeight: "bold", cursor: "pointer" }}
onClick={() => {
if (!itemReadMore) {
setItemDesc(selectedItemContent?.description!)
setItemReadMore(true)
} else {
setItemDesc(selectedItemContent?.description?.slice(0, 200)!)
setItemReadMore(false)
}
}} color="secondary">
{itemReadMore ? "Show Less" : "...Read More"}
</span>
</Typography>
<MainCard title="How To Do">
<List
component="nav"
sx={{
py: 0,
'& .MuiListItemButton-root': {
'& .MuiListItemSecondaryAction-root': { ...actionSX, position: 'relative' }
}
}}
>
{selectedItemContent?.howToDo && selectedItemContent?.howToDo.map((i, index) => {
return (
<>
<ListItemButton divider>
<ListItemAvatar>
<AntAvatar alt="Basic" type="combined" color="info">
{index + 1}
</AntAvatar>
</ListItemAvatar>
<ListItemText primary={<Typography variant="subtitle1">{i}</Typography>} />
</ListItemButton>
</>
)
})}
</List>
</MainCard>
</Grid>
<Grid item xs={4}>
<Grid container spacing={2}>
<Grid item md={12}>
<ButtonGroup aria-label="outlined button group" sx={{ width: "100%" }}>
<Button fullWidth sx={{ width: "100%" }} size="small" variant={itemReferenceData == "image" ? "contained" : "outlined"} onClick={() => { setItemReferenceData("image") }}>Image Reference</Button>
<Button fullWidth sx={{ width: "100%" }} size="small" variant={itemReferenceData == "video" ? "contained" : "outlined"} onClick={() => { setItemReferenceData("video") }}>Vide Reference</Button>
</ButtonGroup>
</Grid>
<Grid item md={12} xs={4} sx={{ height: '246px', '& img': { mb: 0, width: '100%', height: '100%', objectFit: 'contain' } }}>
{itemReferenceData == "image" && <img src={selectedItemContent?.referenceImage} alt="feature" style={{ width: '100%', height: '100%' }} />}
{itemReferenceData == "video" && <>In Constriction ....</>}
</Grid>
</Grid>
</Grid>
<Grid item md={8}>
<Grid container spacing={2}>
<Grid item md={6}>
<ReportCard primary={`Task Mark : ${(selectedItemContent.taskItemMark)?.toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}`}
secondary="Task Mark" color={theme.palette.success.dark} iconPrimary={CheckCircleOutlined}
/>
</Grid>
<Grid item md={6}>
<ReportCard primary={`User Mark : ${(selectedItemContent.taskItemMarkUser)?.toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}`}
secondary="Task User Mark" color={theme.palette.success.dark} iconPrimary={UserOutlined}
/>
</Grid>
<Grid item md={12}>
<ReportCard
primary={`Pass Mark : ${(selectedItemContent?.taskItemMark! * (85 / 100))?.toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}`}
secondary="To Complete the task you should get 85% or higher of the task mark" color={theme.palette.success.dark} iconPrimary={TrophyOutlined}
/>
</Grid>
</Grid>
</Grid>
<Grid item md={4}>
<Grid container spacing={1}>
<Grid item md={12}>
<Button
variant="contained"
endIcon={<PlayCircleOutlined />}
fullWidth
sx={{ my: 1, width: "100%", height: "100%" }}
onClick={() => { handleModelOpen("Practice") }}
color="warning"
size="small"
>
Practice Task
</Button>
</Grid>
<Grid item md={12}>
<Button
variant="contained"
endIcon={<CheckOutlined />}
fullWidth
sx={{ my: 1, width: "100%", height: "100%" }}
onClick={() => { handleModelOpen("Complete") }}
color="success"
size="small"
>
Complete Task
</Button>
</Grid>
<Grid item md={6}>
<Button
variant="contained"
endIcon={<LeftOutlined />}
fullWidth
sx={{ my: 1, width: "100%", height: "100%" }}
onClick={() => { }}
color="secondary"
size="small"
>
Previous Task
</Button>
</Grid>
<Grid item md={6}>
<Button
variant="contained"
endIcon={<RightOutlined />}
fullWidth
sx={{ my: 1, width: "100%", height: "100%" }}
onClick={() => { }}
color={"info"}
size="small"
>
Next Task
</Button>
</Grid>
</Grid>
</Grid>
</>
:
<Grid item md={12}>
<Alert variant="border" color="warning">Select a Task</Alert>
</Grid>
}
</Grid>
</MainCard>
</Grid>
</Grid>
<Dialog
maxWidth="lg"
TransitionComponent={PopupTransition}
keepMounted
fullWidth
onClose={handleModelClose}
open={openModel}
sx={{ '& .MuiDialog-paper': { p: 0 }, transition: 'transform 225ms' }}
aria-describedby="alert-dialog-slide-description"
>
<DialogTitle>{modelOpenFor} Item</DialogTitle>
<Divider />
<DialogContent sx={{ p: 2.5 }}>
<Grid container spacing={2}>
<Grid item md={8}>
<MainCard title="Capture / Upload Image">
<Grid container spacing={2}>
<Grid item md={12}>
<ButtonGroup aria-label="outlined button group" >
<Button fullWidth sx={{ whiteSpace: "nowrap" }} size="small" variant={itemPracticeReferenceData == "capture" ? "contained" : "outlined"} onClick={() => { setItemPracticeReferenceData("capture") }}>Capture Image</Button>
<Button fullWidth sx={{ whiteSpace: "nowrap" }} size="small" variant={itemPracticeReferenceData == "upload" ? "contained" : "outlined"} onClick={() => { setItemPracticeReferenceData("upload") }}>Upload Image</Button>
</ButtonGroup>
</Grid>
{itemPracticeReferenceData == "capture" && <>
<Grid item md={6}>
<Webcam
audio={false}
ref={webcamRef}
screenshotFormat="image/jpeg"
height={300}
width={400}
// videoConstraints={videoConstraints}
/>
</Grid>
<Grid item md={6}>
{capturedImage && (
<img src={capturedImage} alt="Captured" style={{ width: 400, height: 300 }} />
)}
</Grid>
</>}
{itemPracticeReferenceData == "upload" && <>
<Grid item md={12}>
<UploadSingleFile
file={uploadImage}
setFieldValue={function (field: string, value: any): void {
setUploadImage(value)
}}
/>
</Grid>
</>}
</Grid>
</MainCard>
</Grid>
<Grid item md={4}>
<MainCard title="Practice Result">
<Grid container spacing={2}>
<Grid item md={12}>
<ReportCard
primary={`Result : ${itemResult.itemMarkUser ? itemResult.itemMarkUser : "N/A"}`}
secondary="To Complete the task you should get 85% or higher of the task mark" color={theme.palette.success.dark} />
</Grid>
<Grid item md={12}>
<ReportCard
primary={`Status : ${itemResult.status ? itemResult.status : 'N/A'}`}
secondary="To Complete the task you should get 85% or higher of the task mark" color={theme.palette.success.dark} />
</Grid>
<Grid item md={12}>
<Button variant="contained" color="success" onClick={() => { TaskTrigger(uploadImage, webcamRef.current?.getScreenshot(), selectedItemContent?.title!) }} fullWidth disabled={isLoading && isLoadingUserProgress}>
{isLoading && isLoadingUserProgress ? <CircularWithPath /> : <>{modelOpenFor} Task</>}
</Button>
</Grid>
</Grid>
</MainCard>
</Grid>
<Grid item md={8}>
{itemPracticeReferenceData == "capture" && <Button size="small" variant="contained" onClick={capture} fullWidth>
Capture
</Button>
}
</Grid>
<Grid item md={4} />
</Grid>
</DialogContent>
<Divider />
<DialogActions sx={{ p: 2.5 }}>
<Grid container spacing={2}>
<Grid item md={12}>
<Button type="button" variant="contained" sx={{ float: "right" }} onClick={handleModelClose}>
Close
</Button>
</Grid>
</Grid>
</DialogActions>
</Dialog>
</>
)
}
export default Tutorial;
import { taskItemTypeUserProgress } from "types/userProgress";
export interface selectedItemContentProps extends taskItemTypeUserProgress {
userId: string
curriculumCode: string
tutorialCode: string
}
export interface selectedCommonDataProps {
userId: string
curriculumCode: string
tutorialCode: string
title: string
}
export interface itemResultProps {
itemMarkUser: number
status: string
}
\ No newline at end of file
......@@ -21,12 +21,15 @@ import {
import usePagination from 'hooks/usePagination';
import CurriculumCard from 'sections/learning-management/learning-curriculums/CurriculumCard';
import EmptyCurriculumCard from 'sections/learning-management/learning-curriculums/skeleton/EmptyCurriculumCard';
import { useDispatch, useSelector } from 'store';
import { fetchCurriculums, toInitialState } from 'store/reducers/curriculum';
import { openSnackbar } from 'store/reducers/snackbar';
import { curriculumType } from 'types/curriculum';
import { GlobalFilter } from 'utils/react-table';
import { dataProps } from './types/types';
// types
// assets
// assets
// ==============================|| List ||============================== //
......@@ -37,64 +40,22 @@ const allColumns = [
},
{
id: 2,
header: 'Code'
},
{
id: 3,
header: 'Name'
},
{
id: 4,
header: 'Level'
},
{
id: 5,
header: 'Status'
header: 'Title'
}
];
const List = () => {
const data: dataProps[] = [
{
"_id": "1",
"curriculumCode": "01",
"curriculumLevel": 1,
"curriculumDescription": "This curriculum teaches basic sign language skills to help beginners communicate effectively using sign language.",
"curriculumName": "Learn Basic Sign Language Skills",
"curriculumImage": "https://drive.google.com/uc?export=view&id=1YACBlu7X-O7-DKv5DoW3AM9kgfT7Yhdc",
"tutorials": [],
"status": 1,
"createdAt": new Date("2023-08-30T12:00:00Z"),
},
{
"_id": "2",
"curriculumCode": "02",
"curriculumLevel": 2,
"curriculumName": "Learn Intermediate Sign Language Skills",
"curriculumDescription": "This curriculum focuses on building intermediate sign language skills, allowing learners to engage in more complex conversations and interactions using sign language.",
"curriculumImage": "https://drive.google.com/uc?export=view&id=1YACBlu7X-O7-DKv5DoW3AM9kgfT7Yhdc",
"tutorials": [],
"status": 1,
"createdAt": new Date("2023-08-30T12:00:00Z"),
},
{
"_id": "3",
"curriculumCode": "03",
"curriculumLevel": 3,
"curriculumName": "Learn Advance Sign Language Skills",
"curriculumDescription": "This curriculum is designed for those who already have a solid foundation in sign language. It covers advanced topics, nuances, and cultural aspects of sign language communication.",
"curriculumImage": "https://drive.google.com/uc?export=view&id=1YACBlu7X-O7-DKv5DoW3AM9kgfT7Yhdc",
"tutorials": [],
"status": 1,
"createdAt": new Date("2023-08-30T12:00:00Z"),
}
]
const dispatch = useDispatch();
const { curriculums, error, success, isLoading } = useSelector(state => state.curriculum);
const { success: userProgressSuccess } = useSelector(state => state.userProgress);
const [data, setData] = useState<curriculumType[]>([])
const matchDownSM = useMediaQuery((theme: Theme) => theme.breakpoints.down('sm'));
const [curriculumsData, setCurriculumsData] = useState<curriculumType[]>([]);
const [sortBy, setSortBy] = useState('Default');
const [globalFilter, setGlobalFilter] = useState('');
const [curriculumCard, setCurriculumCard] = useState<dataProps[]>([]);
const [page, setPage] = useState(1);
const handleChange = (event: SelectChangeEvent) => {
......@@ -105,27 +66,79 @@ const List = () => {
useEffect(() => {
const newData = data.filter((value: any) => {
if (globalFilter) {
return value.curriculumName.toLowerCase().includes(globalFilter.toLowerCase());
return value.curriculumTitle.toLowerCase().includes(globalFilter.toLowerCase());
} else {
return value;
}
});
console.log(newData.length);
setCurriculumCard(newData);
}, [globalFilter]);
setCurriculumsData(newData);
}, [globalFilter, data]);
const PER_PAGE = 6;
const count = Math.ceil(curriculumCard.length / PER_PAGE);
const _DATA = usePagination(curriculumCard, PER_PAGE);
const count = Math.ceil(curriculumsData.length / PER_PAGE);
const _DATA = usePagination(curriculumsData, PER_PAGE);
const handleChangePage = (e: any, p: any) => {
setPage(p);
_DATA.jump(p);
};
/**
* API Config
* Curriculum API
*/
useEffect(() => {
dispatch(fetchCurriculums());
}, [dispatch, userProgressSuccess]);
useEffect(() => {
setData(curriculums);
}, [curriculums])
// handel error
useEffect(() => {
if (error != null) {
dispatch(
openSnackbar({
open: true,
//@ts-ignore
message: error ? error.Message : "Something went wrong ...",
variant: 'alert',
alert: {
color: 'error'
},
close: true
})
);
dispatch(toInitialState())
}
}, [error])
// handel success
useEffect(() => {
if (success != null) {
dispatch(
openSnackbar({
open: true,
message: success,
variant: 'alert',
alert: {
color: 'success'
},
close: true
})
);
dispatch(toInitialState())
}
}, [success])
if (isLoading) {
return <div>Loading...</div>;
}
return (
<>
<Box sx={{ position: 'relative', marginBottom: 3 }}>
......@@ -168,19 +181,14 @@ const List = () => {
</Stack>
</Box>
<Grid container spacing={3}>
{curriculumCard.length > 0 ? (
{curriculumsData.length > 0 ? (
_DATA
.currentData()
.sort(function (a: any, b: any) {
if (sortBy === 'Customer Name') return a.fatherName.localeCompare(b.fatherName);
if (sortBy === 'Email') return a.email.localeCompare(b.email);
if (sortBy === 'Contact') return a.contact.localeCompare(b.contact);
if (sortBy === 'Age') return b.age < a.age ? 1 : -1;
if (sortBy === 'Country') return a.country.localeCompare(b.country);
if (sortBy === 'Status') return a.status.localeCompare(b.status);
if (sortBy === 'Title') return a.curriculumTitle.localeCompare(b.curriculumTitle);
return a;
})
.map((curriculum: dataProps, index: number) => (
.map((curriculum: curriculumType, index: number) => (
<Slide key={index} direction="up" in={true} timeout={50}>
<Grid item xs={12} sm={6} lg={4}>
<CurriculumCard curriculum={curriculum} />
......@@ -188,7 +196,7 @@ const List = () => {
</Slide>
))
) : (
<EmptyCurriculumCard title={'System have no curriculums yet.'} />
<EmptyCurriculumCard title={'System have no curriculumsData yet.'} />
)}
</Grid>
<Stack spacing={2} sx={{ p: 2.5 }} alignItems="flex-end">
......
import { MouseEvent, useMemo } from 'react';
import { MouseEvent, useEffect, useMemo, useState } from 'react';
// material-ui
import {
......@@ -31,7 +31,12 @@ import {
import { DeleteTwoTone, EditTwoTone } from '@ant-design/icons';
//types
import { ReactTableProps, dataProps } from './types/types';
import status from 'data/status';
import { useDispatch, useSelector } from 'store';
import { openSnackbar } from 'store/reducers/snackbar';
import { fetchUsers, toInitialState } from 'store/reducers/user';
import { Users } from 'types/user';
import { ReactTableProps } from './types/types';
// ==============================|| REACT TABLE ||============================== //
......@@ -101,10 +106,10 @@ function ReactTable({ columns, data }: ReactTableProps) {
);
})
) : (
<EmptyTable msg="No Data" colSpan={5} />
<EmptyTable msg="No Data" colSpan={12} />
)}
<TableRow>
<TableCell sx={{ p: 2 }} colSpan={5}>
<TableCell sx={{ p: 2 }} colSpan={12}>
<TablePagination gotoPage={gotoPage} rows={rows} setPageSize={setPageSize} pageIndex={pageIndex} pageSize={pageSize} />
</TableCell>
</TableRow>
......@@ -119,18 +124,22 @@ function ReactTable({ columns, data }: ReactTableProps) {
const List = () => {
const theme = useTheme();
const dispatch = useDispatch();
const { users, error, success, isLoading } = useSelector(state => state.user);
// table
const data: dataProps[] = [
{
id: 1,
userName: "Nuwan Gamage",
userContactNumber: "0768523456",
userEmailAddress: "nuwangamage@gmail.com",
userType: "Member",
userStatus: "Active"
}
]
// const data: dataProps[] = [
// {
// id: 1,
// userName: "Nuwan Gamage",
// userContactNumber: "0768523456",
// userEmailAddress: "nuwangamage@gmail.com",
// userType: "Member",
// userStatus: "Active"
// }
// ]
const [data, setData] = useState<Users[]>([])
const columns = useMemo(
() =>
......@@ -155,33 +164,69 @@ const List = () => {
},
{
Header: 'User Name',
accessor: 'userName',
className: 'cell-center',
accessor: 'firstName',
Cell: ({ row }: { row: Row }) => {
if (row.values.firstName === undefined || row.values.firstName === null || row.values.firstName === '') {
return <>-</>
}
if (typeof row.values.firstName === 'string') {
//@ts-ignore
return <>{row.values.firstName + " " + row.original.lastName}</>;
}
if (typeof row.values.firstName === 'number') {
return <>{row.values.firstName}</>;
}
// Handle any other data types if necessary
return <>-</>;
}
},
{
Header: 'User Email',
accessor: 'userEmailAddress',
className: 'cell-center',
accessor: 'email',
},
{
Header: 'User Contact Number',
accessor: 'userContactNumber',
className: 'cell-center',
accessor: 'contactNumber',
},
{
Header: 'User Type',
accessor: 'userType',
className: 'cell-center',
accessor: 'type',
Cell: ({ row }: { row: Row }) => {
if (row.values.type === undefined || row.values.type === null || row.values.type === '') {
return <>-</>
}
if (typeof row.values.type === 'string') {
return <>{row.values.type.toUpperCase()}</>;
}
if (typeof row.values.type === 'number') {
return <>{row.values.type}</>;
}
// Handle any other data types if necessary
return <>-</>;
}
},
{
Header: 'User Status',
accessor: 'userStatus',
accessor: 'states',
className: 'cell-center',
Cell: ({ row }: { row: Row }) => {
if (row.values.states === undefined || row.values.states === null || row.values.states === '') {
return <>-</>
}
if (typeof row.values.states === 'string') {
return <>{status.find(status => status.id === parseInt(row.values.states))?.description || "-"}</>;
}
if (typeof row.values.states === 'number') {
return <>{status.find(status => status.id === row.values.states)?.description || "-"}</>;
}
// Handle any other data types if necessary
return <>-</>;
}
},
{
id: "actions",
Header: 'Actions',
accessor: 'actions',
accessor: 'actions',
Cell: ({ row }: { row: Row }) => {
return (
<>
......@@ -225,6 +270,59 @@ const List = () => {
[]
);
/**
* API Config
* User API
*/
useEffect(() => {
dispatch(fetchUsers('member'));
}, [dispatch]);
useEffect(() => {
setData(users);
}, [users])
// handel error
useEffect(() => {
if (error != null) {
dispatch(
openSnackbar({
open: true,
//@ts-ignore
message: error ? error.Message : "Something went wrong ...",
variant: 'alert',
alert: {
color: 'error'
},
close: true
})
);
dispatch(toInitialState())
}
}, [error])
// handel success
useEffect(() => {
if (success != null) {
dispatch(
openSnackbar({
open: true,
message: success,
variant: 'alert',
alert: {
color: 'success'
},
close: true
})
);
dispatch(toInitialState())
}
}, [success])
if (isLoading) {
return <div>Loading...</div>;
}
return (
<>
<MainCard content={false}>
......
import { Column } from 'react-table';
import { Users } from 'types/user';
export interface dataProps {
id: number | string | undefined
......@@ -11,7 +12,7 @@ export interface dataProps {
export interface ReactTableProps {
columns: Column[]
data: dataProps[]
data: Users[]
}
export interface userProps {
......
import { MouseEvent, useMemo, useState } from 'react';
import { MouseEvent, useEffect, useMemo, useState } from 'react';
// material-ui
import {
......@@ -34,9 +34,15 @@ import { DeleteTwoTone, EditTwoTone, EyeTwoTone, PlusOutlined } from '@ant-desig
//types
import { PopupTransition } from 'components/@extended/Transitions';
import curriculumLevels from 'data/curriculumLevels';
import status from 'data/status';
import AddEditCurriculum from 'sections/parameters/curriculum-management/AddEditCurriculum';
import AlertCurriculumDelete from 'sections/parameters/curriculum-management/AlertCurriculumDelete';
import { ReactTableProps, curriculumProps, dataProps } from './types/types';
import { useDispatch, useSelector } from 'store';
import { fetchCurriculums, toInitialState } from 'store/reducers/curriculum';
import { openSnackbar } from 'store/reducers/snackbar';
import { Curriculum, Curriculums } from 'types/curriculum';
import { ReactTableProps } from './types/types';
// ==============================|| REACT TABLE ||============================== //
......@@ -122,16 +128,21 @@ function ReactTable({ columns, data, handleAddEdit }: ReactTableProps) {
// ==============================|| List ||============================== //
const List = () => {
const theme = useTheme();
const dispatch = useDispatch();
const { curriculums, error, success, isLoading } = useSelector(state => state.curriculum);
// table
const data: dataProps[] = []
// const data: dataProps[] = []
// table
const [data, setData] = useState<Curriculums[]>([])
const columns = useMemo(
() =>
[
{
Header: '#',
accessor: 'id',
accessor: '_id',
className: 'cell-center',
Cell: ({ row }: { row: Row }) => {
if (row.id === undefined || row.id === null || row.id === '') {
......@@ -150,23 +161,76 @@ const List = () => {
{
Header: 'Curriculum Code',
accessor: 'curriculumCode'
},
},
{
Header: 'Curriculum LVL',
accessor: 'curriculumLevel'
},
accessor: 'curriculumLevel',
Cell: ({ row }: { row: Row }) => {
if (row.values.curriculumLevel === undefined || row.values.curriculumLevel === null || row.values.curriculumLevel === '') {
return <>-</>
}
if (typeof row.values.curriculumLevel === 'string') {
return <>{row.values.curriculumLevel}</>;
}
if (typeof row.values.curriculumLevel === 'number') {
return <>{curriculumLevels.find(curriculumLevel => curriculumLevel.id === row.values.curriculumLevel)?.description || "-"}</>;
}
// Handle any other data types if necessary
return <>-</>;
}
},
{
Header: 'Curriculum Name',
accessor: 'curriculumTitle'
},
accessor: 'curriculumTitle',
Cell: ({ row }: { row: Row }) => {
if (row.values.curriculumTitle === undefined || row.values.curriculumTitle === null || row.values.curriculumTitle === '') {
return <>-</>
}
if (typeof row.values.curriculumTitle === 'string') {
return <>{row.values.curriculumTitle}</>;
}
if (typeof row.values.curriculumTitle === 'number') {
return <>{row.values.curriculumTitle || "-"}</>;
}
// Handle any other data types if necessary
return <>-</>;
}
},
{
Header: 'Status',
accessor: 'status'
},
accessor: 'status',
className: 'cell-center',
Cell: ({ row }: { row: Row }) => {
if (row.values.status === undefined || row.values.status === null || row.values.status === '') {
return <>-</>
}
if (typeof row.values.status === 'string') {
return <>{row.values.status}</>;
}
if (typeof row.values.status === 'number') {
return <>{status.find(status => status.id === row.values.status)?.description || "-"}</>;
}
// Handle any other data types if necessary
return <>-</>;
}
},
{
Header: 'Created By',
accessor: 'createdBy'
},
accessor: 'createdBy',
Cell: ({ row }: { row: Row }) => {
if (row.values.createdBy === undefined || row.values.createdBy === null || row.values.createdBy === '') {
return <>-</>
}
if (typeof row.values.createdBy === 'string') {
return <>{row.values.createdBy}</>;
}
if (typeof row.values.createdBy === 'number') {
return <>{row.values.createdBy}</>;
}
// Handle any other data types if necessary
return <>-</>;
}
},
{
id: "actions",
Header: 'Actions',
......@@ -209,6 +273,7 @@ const List = () => {
onClick={(e: MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
setCurriculumId(row.values._id)
setCurriculumTitle(row.values.curriculumTitle)
setOpenAlert(true)
}}
>
......@@ -226,7 +291,7 @@ const List = () => {
//dialog model
const [addEdit, setAddEdit] = useState<boolean>(false);
const [curriculum, setCurriculum] = useState<curriculumProps>();
const [curriculum, setCurriculum] = useState<Curriculum>();
const handleAddEdit = () => {
setAddEdit(!addEdit);
......@@ -235,12 +300,66 @@ const List = () => {
//alert model
const [openAlert, setOpenAlert] = useState(false);
const [curriculumId, setCurriculumId] = useState<number | string | undefined>(undefined)
const [curriculumId, setCurriculumId] = useState<string | undefined>(undefined)
const [curriculumTitle, setCurriculumTitle] = useState<string | undefined>(undefined)
const handleAlertClose = () => {
setOpenAlert(!openAlert);
};
/**
* API Config
* Curriculum API
*/
useEffect(() => {
dispatch(fetchCurriculums());
}, [dispatch]);
useEffect(() => {
setData(curriculums);
}, [curriculums])
// handel error
useEffect(() => {
if (error != null) {
dispatch(
openSnackbar({
open: true,
//@ts-ignore
message: error ? error.Message : "Something went wrong ...",
variant: 'alert',
alert: {
color: 'error'
},
close: true
})
);
dispatch(toInitialState())
}
}, [error])
// handel success
useEffect(() => {
if (success != null) {
dispatch(
openSnackbar({
open: true,
message: success,
variant: 'alert',
alert: {
color: 'success'
},
close: true
})
);
dispatch(toInitialState())
}
}, [success])
if (isLoading) {
return <div>Loading...</div>;
}
return (
<>
<MainCard content={false}>
......@@ -258,10 +377,10 @@ const List = () => {
sx={{ '& .MuiDialog-paper': { p: 0 }, transition: 'transform 225ms' }}
aria-describedby="alert-dialog-slide-description"
>
<AddEditCurriculum curriculum={curriculum} onCancel={handleAddEdit} />
<AddEditCurriculum curriculum={curriculum!} onCancel={handleAddEdit} />
</Dialog>
{/* alert model */}
{!curriculum && <AlertCurriculumDelete title={""} open={openAlert} handleClose={handleAlertClose} deleteId={curriculumId} />}
{!curriculum && <AlertCurriculumDelete title={curriculumTitle!} open={openAlert} handleClose={handleAlertClose} deleteId={curriculumId} />}
</MainCard>
</>
)
......
import { Column } from 'react-table';
import { Curriculums } from 'types/curriculum';
export interface dataProps {
_id: number | string | undefined;
......@@ -17,7 +18,7 @@ export interface dataProps {
export interface ReactTableProps {
columns: Column[]
data: dataProps[]
data: Curriculums[]
handleAddEdit: () => void
}
......
import { MouseEvent, useMemo, useState } from 'react';
import { MouseEvent, useEffect, useMemo, useState } from 'react';
// material-ui
import {
......@@ -34,9 +34,14 @@ import {
import { DeleteTwoTone, EditTwoTone, EyeTwoTone, PlusOutlined } from '@ant-design/icons';
//types
import status from 'data/status';
import AddEditTutorial from 'sections/parameters/tutorial-management/AddEditTutorial';
import AlertTutorialDelete from 'sections/parameters/tutorial-management/AlertTutorialDelete';
import { ReactTableProps, dataProps, tutorialProps } from './types/types';
import { useDispatch, useSelector } from 'store';
import { openSnackbar } from 'store/reducers/snackbar';
import { fetchTutorials, toInitialState } from 'store/reducers/tutorial';
import { Tutorial, Tutorials } from 'types/tutorial';
import { ReactTableProps } from './types/types';
// ==============================|| REACT TABLE ||============================== //
......@@ -122,16 +127,158 @@ function ReactTable({ columns, data, handleAddEdit }: ReactTableProps) {
// ==============================|| List ||============================== //
const List = () => {
const theme = useTheme();
const dispatch = useDispatch();
const { tutorials, error, success, isLoading } = useSelector(state => state.tutorial);
// table
// const data: dataProps[] = [
// {
// "_id": "",
// "tutorialCode": "01",
// "tutorialTitle": "Numbers and Counting in Sign Language",
// "tutorialDescription": "In this tutorial, you'll discover how to express numbers visually using simple hand gestures. Each number has a unique sign that involves specific finger placements and hand movements. We'll break down each number step by step, providing you with clear instructions, images, and videos to help you learn effectively.",
// "tutorialImage": "https://drive.google.com/uc?export=view&id=1YACBlu7X-O7-DKv5DoW3AM9kgfT7Yhdc",
// "taskItems": [
// {
// "_id": "",
// "title": "Learn Number One",
// "description": "Learn how to sign the number one in sign language.",
// "howToDo": "- Extend your index finger straight up.\n- Keep the rest of your fingers closed.\n- Hold your hand in front of your chest.",
// "referenceImage": "https://example.com/number_one.jpg",
// "referenceVideo": "https://example.com/number_one_video.mp4",
// "taskItemMark": 10
// },
// {
// "_id": "",
// "title": "Learn Number Two",
// "description": "Learn how to sign the number two in sign language.",
// "howToDo": "- Extend your index and middle fingers straight up.\n- Keep the rest of your fingers closed.\n- Hold your hand in front of your chest.",
// "referenceImage": "https://example.com/number_two.jpg",
// "referenceVideo": "https://example.com/number_two_video.mp4",
// "taskItemMark": 10
// },
// {
// "_id": "",
// "title": "Learn Number Three",
// "description": "Learn how to sign the number three in sign language.",
// "howToDo": "- Extend your index, middle, and ring fingers straight up.\n- Keep the rest of your fingers closed.\n- Hold your hand in front of your chest.",
// "referenceImage": "https://example.com/number_three.jpg",
// "referenceVideo": "https://example.com/number_three_video.mp4",
// "taskItemMark": 10
// },
// {
// "_id": "",
// "title": "Learn Number Four",
// "description": "Learn how to sign the number four in sign language.",
// "howToDo": "- Extend your thumb, index, middle, and ring fingers straight up.\n- Keep your pinky finger folded.\n- Hold your hand in front of your chest.",
// "referenceImage": "https://example.com/number_four.jpg",
// "referenceVideo": "https://example.com/number_four_video.mp4",
// "taskItemMark": 10
// },
// {
// "_id": "",
// "title": "Learn Number Five",
// "description": "Learn how to sign the number five in sign language.",
// "howToDo": "- Extend all your fingers straight up.\n- Keep your thumb resting on the side of your palm.\n- Hold your hand in front of your chest.",
// "referenceImage": "https://example.com/number_five.jpg",
// "referenceVideo": "https://example.com/number_five_video.mp4",
// "taskItemMark": 10
// },
// {
// "_id": "",
// "title": "Learn Number Six",
// "description": "Learn how to sign the number six in sign language.",
// "howToDo": "- Extend your thumb and pinky finger straight up.\n- Keep the rest of your fingers closed.\n- Hold your hand in front of your chest.",
// "referenceImage": "https://example.com/number_six.jpg",
// "referenceVideo": "https://example.com/number_six_video.mp4",
// "taskItemMark": 10
// },
// {
// "_id": "",
// "title": "Learn Number Seven",
// "description": "Learn how to sign the number seven in sign language.",
// "howToDo": "- Extend your index, middle, and ring fingers straight up.\n- Keep your thumb, pinky, and pinky finger folded.\n- Hold your hand in front of your chest.",
// "referenceImage": "https://example.com/number_seven.jpg",
// "referenceVideo": "https://example.com/number_seven_video.mp4",
// "taskItemMark": 10
// },
// {
// "_id": "",
// "title": "Learn Number Eight",
// "description": "Learn how to sign the number eight in sign language.",
// "howToDo": "- Extend all your fingers straight up.\n- Cross your index and middle fingers over your ring and pinky fingers.\n- Hold your hand in front of your chest.",
// "referenceImage": "https://example.com/number_eight.jpg",
// "referenceVideo": "https://example.com/number_eight_video.mp4",
// "taskItemMark": 10
// },
// {
// "_id": "",
// "title": "Learn Number Nine",
// "description": "Learn how to sign the number nine in sign language.",
// "howToDo": "- Extend your thumb and all your fingers straight up.\n- Keep your pinky finger folded.\n- Hold your hand in front of your chest.",
// "referenceImage": "https://example.com/number_nine.jpg",
// "referenceVideo": "https://example.com/number_nine_video.mp4",
// "taskItemMark": 10
// },
// {
// "_id": "",
// "title": "Learn Number Ten",
// "description": "Learn how to sign the number ten in sign language.",
// "howToDo": "- Extend your thumb, index, and middle fingers straight up.\n- Keep the rest of your fingers closed.\n- Hold your hand in front of your chest.",
// "referenceImage": "https://example.com/number_ten.jpg",
// "referenceVideo": "https://example.com/number_ten_video.mp4",
// "taskItemMark": 10
// }
// ],
// "status": 1,
// "createdAt": new Date("2023-08-30T12:00:00Z"),
// "createdBy": "Nuwan Gamage"
// },
// {
// "_id": "",
// "tutorialCode": "02",
// "tutorialTitle": "Everyday Vocabulary in Sign Language",
// "tutorialDescription": "Teach signs for everyday objects and activities, such as eat, drink, sleep, book, pen, etc.\nIntroduce signs for common words used in daily life.\nProvide visual demonstrations and interactive exercises for learners to practice using these signs.",
// "tutorialImage": "https://drive.google.com/uc?export=view&id=1YACBlu7X-O7-DKv5DoW3AM9kgfT7Yhdc",
// "taskItems": [],
// "status": 1,
// "createdAt": new Date("2023-08-30T12:00:00Z"),
// "createdBy": "Nuwan Gamage"
// },
// {
// "_id": "",
// "tutorialCode": "03",
// "tutorialTitle": "Family Signs in Sign Language",
// "tutorialDescription": "Teach signs for family members, such as mother, father, sister, brother, etc.\nIntroduce signs for common family-related words, such as family, love, and home.\nProvide visual demonstrations and practice exercises for learners to practice these family signs.",
// "tutorialImage": "https://drive.google.com/uc?export=view&id=1YACBlu7X-O7-DKv5DoW3AM9kgfT7Yhdc",
// "taskItems": [],
// "status": 1,
// "createdAt": new Date("2023-08-30T12:00:00Z"),
// "createdBy": "Nuwan Gamage"
// },
// {
// "_id": "",
// "tutorialCode": "04",
// "tutorialTitle": "Basic Conversational Phrases in Sign Language",
// "tutorialDescription": "Teach simple conversational phrases, such as \"What is your name?\" or \"How are you?\"\nIntroduce signs for common question words and phrases.\nProvide visual demonstrations and practice exercises for learners to practice these conversational phrases.",
// "tutorialImage": "https://drive.google.com/uc?export=view&id=1YACBlu7X-O7-DKv5DoW3AM9kgfT7Yhdc",
// "taskItems": [],
// "status": 1,
// "createdAt": new Date("2023-08-30T12:00:00Z"),
// "createdBy": "Nuwan Gamage"
// }
// ]
// table
const data: dataProps[] = []
const [data, setData] = useState<Tutorials[]>([])
const columns = useMemo(
() =>
[
{
Header: '#',
accessor: 'id',
accessor: '_id',
className: 'cell-center',
Cell: ({ row }: { row: Row }) => {
if (row.id === undefined || row.id === null || row.id === '') {
......@@ -157,11 +304,38 @@ const List = () => {
},
{
Header: 'Status',
accessor: 'status'
accessor: 'status',
className: 'cell-center',
Cell: ({ row }: { row: Row }) => {
if (row.values.status === undefined || row.values.status === null || row.values.status === '') {
return <>-</>
}
if (typeof row.values.status === 'string') {
return <>{row.values.status}</>;
}
if (typeof row.values.status === 'number') {
return <>{status.find(status => status.id === row.values.status)?.description || "-"}</>;
}
// Handle any other data types if necessary
return <>-</>;
}
},
{
Header: 'Created By',
accessor: 'createdBy'
accessor: 'createdBy',
Cell: ({ row }: { row: Row }) => {
if (row.values.createdBy === undefined || row.values.createdBy === null || row.values.createdBy === '') {
return <>-</>
}
if (typeof row.values.createdBy === 'string') {
return <>{row.values.createdBy}</>;
}
if (typeof row.values.createdBy === 'number') {
return <>{row.values.createdBy}</>;
}
// Handle any other data types if necessary
return <>-</>;
}
},
{
id: "actions",
......@@ -204,6 +378,7 @@ const List = () => {
color="error"
onClick={(e: MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
setTutorialTitle(row.values.tutorialTitle)
setTutorialId(row.values._id)
setOpenAlert(true)
}}
......@@ -222,7 +397,7 @@ const List = () => {
//dialog model
const [addEdit, setAddEdit] = useState<boolean>(false);
const [tutorial, setTutorial] = useState<tutorialProps>();
const [tutorial, setTutorial] = useState<Tutorial>();
const handleAddEdit = () => {
setAddEdit(!addEdit);
......@@ -231,20 +406,74 @@ const List = () => {
//alert model
const [openAlert, setOpenAlert] = useState(false);
const [tutorialId, setTutorialId] = useState<number | string | undefined>(undefined)
const [tutorialId, setTutorialId] = useState<string | undefined>(undefined)
const [tutorialTitle, setTutorialTitle] = useState<string | undefined>(undefined)
const handleAlertClose = () => {
setOpenAlert(!openAlert);
};
/**
* API Config
* Tutorial API
*/
useEffect(() => {
dispatch(fetchTutorials());
}, [dispatch]);
useEffect(() => {
setData(tutorials);
}, [tutorials])
// handel error
useEffect(() => {
if (error != null) {
dispatch(
openSnackbar({
open: true,
//@ts-ignore
message: error ? error.Message : "Something went wrong ...",
variant: 'alert',
alert: {
color: 'error'
},
close: true
})
);
dispatch(toInitialState())
}
}, [error])
// handel success
useEffect(() => {
if (success != null) {
dispatch(
openSnackbar({
open: true,
message: success,
variant: 'alert',
alert: {
color: 'success'
},
close: true
})
);
dispatch(toInitialState())
}
}, [success])
if (isLoading) {
return <div>Loading...</div>;
}
return (
<>
<MainCard content={false}>
<ScrollX>
<ReactTable columns={columns} data={data} handleAddEdit={handleAddEdit} />
</ScrollX>
{/* add / edit tutorial dialog */}
<Dialog
{/* add / edit tutorial dialog */}
<Dialog
maxWidth="sm"
TransitionComponent={PopupTransition}
keepMounted
......@@ -254,10 +483,10 @@ const List = () => {
sx={{ '& .MuiDialog-paper': { p: 0 }, transition: 'transform 225ms' }}
aria-describedby="alert-dialog-slide-description"
>
<AddEditTutorial tutorial={tutorial} onCancel={handleAddEdit} />
<AddEditTutorial tutorial={tutorial!} onCancel={handleAddEdit} />
</Dialog>
{/* alert model */}
{!tutorial && <AlertTutorialDelete title={""} open={openAlert} handleClose={handleAlertClose} deleteId={tutorialId} />}
{!tutorial && <AlertTutorialDelete title={tutorialTitle!} open={openAlert} handleClose={handleAlertClose} deleteId={tutorialId} />}
</MainCard>
</>
)
......
import { Column } from 'react-table';
import { Tutorials } from 'types/tutorial';
export interface dataProps {
_id: number | string | undefined;
......@@ -6,17 +7,17 @@ export interface dataProps {
tutorialTitle: string;
tutorialDescription: string;
tutorialImage: string;
status: number;
createdBy: string;
updatedBy: string;
createdAt: Date;
updatedAt: Date;
status?: number;
createdBy?: string;
updatedBy?: string;
createdAt?: Date;
updatedAt?: Date;
taskItems: taskItemProps[]
}
export interface ReactTableProps {
columns: Column[]
data: dataProps[]
data: Tutorials[]
handleAddEdit: () => void
}
......@@ -41,4 +42,5 @@ export interface taskItemProps {
howToDo: string;
referenceImage: string;
referenceVideo: string;
taskItemMark: number;
}
\ No newline at end of file
import axios from 'axios';
class SignLanguageToTextService {
predictSignLanguageVideo(speed, data) {
return axios.post(
`http://127.0.0.1:8000/predict-sign-language/video/speed_levels?speed=${speed}`,
data
);
}
}
export default new SignLanguageToTextService();
import axios from 'axios';
class VideoToSignLanguage {
videoTranslation(data) {
return axios.post(
// @ts-ignore
`http://127.0.0.1:8000/translated_items/`,
data
);
}
}
export default new VideoToSignLanguage();
......@@ -154,7 +154,7 @@ const Process = () => {
};
return (
<>
<MainCard content={false}>
<MainCard content={false} title="Translate Sinhala Sign Language into Text Here">
<ScrollX>
{/* content here */}
......@@ -233,7 +233,7 @@ const Process = () => {
step={10}
marks
min={10}
max={110}
max={100}
/>
<h4>Speed - {speed}</h4>
</Grid>
......@@ -331,7 +331,7 @@ const Process = () => {
step={10}
marks
min={10}
max={110}
max={100}
/>
<h4>Speed - {speed}</h4>
</Grid>
......
import { MouseEvent, useMemo, useState } from 'react';
import { MouseEvent, useEffect, useMemo, useState } from 'react';
// material-ui
import {
......@@ -32,11 +32,16 @@ import {
import { DeleteTwoTone, EditTwoTone, PlusOutlined } from '@ant-design/icons';
//types
import { ReactTableProps, dataProps, userProps } from './types/types';
import status from 'data/status';
import { useDispatch, useSelector } from 'store';
import { openSnackbar } from 'store/reducers/snackbar';
import { fetchUsersByType, toInitialState } from 'store/reducers/user';
import { Users } from 'types/user';
import { ReactTableProps, userProps } from './types/types';
// ==============================|| REACT TABLE ||============================== //
function ReactTable({ columns, data , handleAddEdit}: ReactTableProps) {
function ReactTable({ columns, data, handleAddEdit }: ReactTableProps) {
const filterTypes = useMemo(() => renderFilterTypes, []);
const defaultColumn = useMemo(() => ({ Filter: DefaultColumnFilter }), []);
......@@ -102,10 +107,10 @@ function ReactTable({ columns, data , handleAddEdit}: ReactTableProps) {
);
})
) : (
<EmptyTable msg="No Data" colSpan={5} />
<EmptyTable msg="No Data" colSpan={12} />
)}
<TableRow>
<TableCell sx={{ p: 2 }} colSpan={5}>
<TableCell sx={{ p: 2 }} colSpan={12}>
<TablePagination gotoPage={gotoPage} rows={rows} setPageSize={setPageSize} pageIndex={pageIndex} pageSize={pageSize} />
</TableCell>
</TableRow>
......@@ -120,26 +125,33 @@ function ReactTable({ columns, data , handleAddEdit}: ReactTableProps) {
const List = () => {
const theme = useTheme();
const dispatch = useDispatch();
const { users, error, success, isLoading } = useSelector(state => state.user);
// table
const data: dataProps[] = [
{
id: 1,
userName: "Janith Gamage",
userContactNumber: "0768523525",
userEmailAddress: "janithgamage1.ed@gmail.com",
userType: "Admin",
userStatus: "Active"
},
{
id: 1,
userName: "Nuwan Gamage",
userContactNumber: "0768523456",
userEmailAddress: "nuwangamage@gmail.com",
userType: "Member",
userStatus: "Active"
}
]
// const data: dataProps[] = [
// {
// id: 1,
// userName: "Janith Gamage",
// userContactNumber: "0768523525",
// userEmailAddress: "janithgamage1.ed@gmail.com",
// userType: "Admin",
// userStatus: "Active"
// },
// {
// id: 1,
// userName: "Nuwan Gamage",
// userContactNumber: "0768523456",
// userEmailAddress: "nuwangamage@gmail.com",
// userType: "Member",
// userStatus: "Active"
// }
// ]
// table
const [data, setData] = useState<Users[]>([])
//dialog model
const [addEdit, setAddEdit] = useState<boolean>(false);
......@@ -173,28 +185,64 @@ const List = () => {
},
{
Header: 'User Name',
accessor: 'userName',
className: 'cell-center',
accessor: 'firstName',
Cell: ({ row }: { row: Row }) => {
if (row.values.firstName === undefined || row.values.firstName === null || row.values.firstName === '') {
return <>-</>
}
if (typeof row.values.firstName === 'string') {
//@ts-ignore
return <>{row.values.firstName + " " + row.original.lastName}</>;
}
if (typeof row.values.firstName === 'number') {
return <>{row.values.firstName}</>;
}
// Handle any other data types if necessary
return <>-</>;
}
},
{
Header: 'User Email',
accessor: 'userEmailAddress',
className: 'cell-center',
accessor: 'email',
},
{
Header: 'User Contact Number',
accessor: 'userContactNumber',
className: 'cell-center',
accessor: 'contactNumber',
},
{
Header: 'User Type',
accessor: 'userType',
className: 'cell-center',
accessor: 'type',
Cell: ({ row }: { row: Row }) => {
if (row.values.type === undefined || row.values.type === null || row.values.type === '') {
return <>-</>
}
if (typeof row.values.type === 'string') {
return <>{row.values.type.toUpperCase()}</>;
}
if (typeof row.values.type === 'number') {
return <>{row.values.type}</>;
}
// Handle any other data types if necessary
return <>-</>;
}
},
{
Header: 'User Status',
accessor: 'userStatus',
accessor: 'states',
className: 'cell-center',
Cell: ({ row }: { row: Row }) => {
if (row.values.states === undefined || row.values.states === null || row.values.states === '') {
return <>-</>
}
if (typeof row.values.states === 'string') {
return <>{status.find(status => status.id === parseInt(row.values.states))?.description || "-"}</>;
}
if (typeof row.values.states === 'number') {
return <>{status.find(status => status.id === row.values.states)?.description || "-"}</>;
}
// Handle any other data types if necessary
return <>-</>;
}
},
{
id: "actions",
......@@ -243,6 +291,59 @@ const List = () => {
[]
);
/**
* API Config
* User API
*/
useEffect(() => {
dispatch(fetchUsersByType());
}, [dispatch]);
useEffect(() => {
setData(users);
}, [users])
// handel error
useEffect(() => {
if (error != null) {
dispatch(
openSnackbar({
open: true,
//@ts-ignore
message: error ? error.Message : "Something went wrong ...",
variant: 'alert',
alert: {
color: 'error'
},
close: true
})
);
dispatch(toInitialState())
}
}, [error])
// handel success
useEffect(() => {
if (success != null) {
dispatch(
openSnackbar({
open: true,
message: success,
variant: 'alert',
alert: {
color: 'success'
},
close: true
})
);
dispatch(toInitialState())
}
}, [success])
if (isLoading) {
return <div>Loading...</div>;
}
return (
<>
<MainCard content={false}>
......
import { Column } from 'react-table';
import { Users } from 'types/user';
export interface dataProps {
id: number | string | undefined
......@@ -11,7 +12,7 @@ export interface dataProps {
export interface ReactTableProps {
columns: Column[]
data: dataProps[]
data: Users[]
handleAddEdit: () => void
}
......
// project import
import {
Box,
Button,
ButtonGroup,
Card,
CardContent,
CardHeader,
Container,
Grid,
IconButton,
InputAdornment,
LinearProgress,
Paper,
// Slider,
Stack,
TextField,
Typography
} from '@mui/material';
import MainCard from 'components/MainCard';
import ScrollX from 'components/ScrollX';
import { CloudUploadOutlined, CopyOutlined, HighlightOutlined, TranslationOutlined } from '@ant-design/icons';
import axios from 'axios';
import { MuiFileInput } from 'mui-file-input';
import { useSnackbar } from 'notistack';
import React, { useState } from 'react';
// import Unity, { UnityContext } from "react-unity-webgl";
// ---------- * Unity Application * ------------------------------
// const unityContext = new UnityContext ({
// loaderUrl: "build/myunityapp.loader.js",
// dataUrl: "build/myunityapp.data",
// frameworkUrl: "build/myunityapp.framework.js",
// codeUrl: "build/myunityapp.wasm",
// });
// ==============================|| List ||============================== //
const VideoTranslate = () => {
const [file, setFile] = useState<File | string | null>(null);
const [videoUrl, setVideoUrl] = useState('');
const [loading, setLoading] = useState(false);
const [value, setValue] = useState('');
const [translatedTextSi, setTranslatedTextSi] = useState('');
const [translatedTextEn, setTranslatedTextEn] = useState('');
const [showUnityWebGL, setShowUnityWebGL] = useState(false);
const handleDropSingleFile = (files: any) => {
if (files) {
setFile(
Object.assign(files, {
preview: URL.createObjectURL(files)
})
);
setVideoUrl(URL.createObjectURL(files));
}
};
const handleChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
setValue(event.target.value);
};
// ----------------- Video Upload ------------------------------------------------
// const TranslateVideoToSignLanguage = async () => {
// if (file) {
// setLoading(true);
// const formData = new FormData();
// //@ts-ignore
// formData.append('video', file, file.name);
// try {
// const response = await VideoToSignLanguageService.videoTranslation(formData);
// const { translated_text_si, translated_text_en } = response.data;
// setTranslatedTextSi(translated_text_si);
// setTranslatedTextEn(translated_text_en);
// if (response.status == 200) {
// console.log(response.data);
// // setValue(response.data.predictions);
// } else {
// enqueueSnackbar('Something went Wrong!', { variant: 'error' });
// }
// // setLoading(false);
// } catch (error) {
// console.log(error);
// setLoading(false);
// enqueueSnackbar('Something went Wrong!', { variant: 'error' });
// }
// } else {
// enqueueSnackbar('Please select a file.', { variant: 'warning' });
// }
// };
async function uploadVideo() {
setLoading(true)
if (file) {
const formData = new FormData();
formData.append('file', file);
try {
const response = await axios.post('http://127.0.0.1:8000/rest_pyton/uploaded_video', formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
});
setTranslatedTextEn(response.data.translated_text_en)
setTranslatedTextSi(response.data.translated_text_si)
// Show the Unity WebGL build
setShowUnityWebGL(true);
setLoading(false)
} catch (error) {
console.error('Error:', error);
setLoading(false)
}
} else {
console.error('No file selected.');
setLoading(false)
}
}
const { enqueueSnackbar } = useSnackbar();
const onCopy = (text: string) => {
if (text) {
navigator.clipboard.writeText(text);
enqueueSnackbar('Copied!', { variant: 'success' });
}
};
return (
<MainCard content={false}>
<ScrollX>
{/* Content Here */}
<Container
sx={{
padding: 3,
bgcolor: '#625D5D',
color: '#fafafb',
borderRadius: 6,
// boxShadow: '0px 4px 6px rgba(0, 0, 0, 0.1)' // Subtle box shadow
}}
>
{/* Double Button Here */}
<ButtonGroup
disableElevation
variant="contained"
aria-label="Customized buttons"
sx={{
marginBottom: '20px',
backgroundColor: '#ff3c3c', // Change background color
'& .MuiButton-root': { // Apply styles to individual buttons
color: 'white', // Text color
'&:hover': {
backgroundColor: '#000000' // Change color on hover
}
}
}}
>
<Button
sx={{
bgcolor: '#ff3c3c',
padding: '10px 50px',
fontSize: '1.05rem', // Larger font size
'& .anticon': {
fontSize: '1.2rem', // Larger icon size
},
}}
// variant={checkTranalationTypeForUpload()}
startIcon={<CloudUploadOutlined />}
// onClick={() => {
// setIsUploadFile(true);
// }}
>
Upload
</Button>
<Button
sx={{
bgcolor: '#ff3c3c',
padding: '10px 50px',
fontSize: '1.05rem', // Larger font size
'& .anticon': {
fontSize: '1.2rem', // Larger icon size
},
}}
// variant={checkTranalationTypeForRecord()}
startIcon={<HighlightOutlined />}
// onClick={() => {
// setIsUploadFile(false);
// }}
>
Text
</Button>
</ButtonGroup>
{/* Video uploading */}
<Box sx={{ flexGrow: 1 }}>
<Card>
<CardHeader title="Upload a video | Drag & Drop or Select File" />
<Grid container spacing={2}>
<Grid item xs={12} md={6}>
<Card sx={{ marginBottom: '20px', marginLeft: '10px', padding: '35px 10px' }}>
<CardContent>
{/* ! Important */}
{/* @ts-ignore */}
<MuiFileInput value={file} onChange={handleDropSingleFile} inputProps={{ accept: 'video/*' }} />
<Paper style={{ padding: '20px', marginTop: '15px' }}>
<Typography variant="h5" align="center" gutterBottom>
Preview
</Typography>
<div style={{ marginTop: '20px', textAlign: 'center' }}>
{file ? <video src={videoUrl} width="400" controls /> : <p>No Video Selected ...</p>}
</div>
</Paper>
</CardContent>
</Card>
</Grid>
<Grid item xs={12} md={6}>
<Card sx={{ p: 7, minHeight: 300, marginBottom: '10px', marginRight: '10px' }}>
<Box display="grid" gap={5}>
<Stack spacing={2}>
<Grid container spacing={1}>
{/* <Grid item xs={12} md={6}> */}
{/* <h3>Set Sign Speed </h3> */}
{/* <Slider
defaultValue={30}
getAriaValueText={valuetext}
valueLabelDisplay="auto"
step={10}
marks
min={10}
max={110}
/> */}
{/* <h4>Speed - {speed}</h4> */}
{/* </Grid> */}
<Grid item xs={12} md={6} container direction="row" justifyContent="flex-start" alignItems="center">
<Button
variant="contained"
style={{ width: '200px', height: '60px', fontSize: '20px' }}
sx={{
mb: 3
}}
disabled={loading}
onClick={uploadVideo}
endIcon={<TranslationOutlined />}
>
Translate
</Button>
{/* ... other JSX ... */}
{/* Conditionally render the Unity WebGL build */}
{showUnityWebGL && (
<iframe
src="https://64f66d39fdef493229b2ddd9--lambent-unicorn-97396a.netlify.app/"
width="700px"
height="700px" // Adjust the height as needed
title="Unity WebGL"
style={{ border: 'none', overflow: 'hidden' }}
></iframe>
)}
{/* ... other JSX ... */}
{/* -------- Translated Sinhala Unicode ------------------------- */}
<Typography variant="overline" sx={{ color: 'text.secondary', fontStyle: 'italic', marginBottom: 2 }}>
Sinhala Unicode
</Typography>
<TextField
sx={{
marginBottom: 2
}}
fullWidth
value={translatedTextSi}
onChange={handleChange}
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton onClick={() => onCopy(value)}>
<CopyOutlined />
</IconButton>
</InputAdornment>
)
}}
/>
{/* -------- Translated English Unicode ------------------------- */}
<Typography variant="overline" sx={{ color: 'text.secondary', fontStyle: 'italic' }}>
English Unicode
</Typography>
<TextField
fullWidth
value={translatedTextEn}
onChange={handleChange}
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton onClick={() => onCopy(value)}>
<CopyOutlined />
</IconButton>
</InputAdornment>
)
}}
/>
{/* ----------------------------- */}
</Grid>
</Grid>
{loading ? (
<Card>
<CardContent>
<LinearProgress />
<center>
<Typography variant="h5" component="div" sx={{ marginTop: 2 }}>
Loading...
</Typography>
</center>
</CardContent>
</Card>
) : (
<div>
{/* -------- Translated Sinhala Unicode ------------------------- */}
{/* <Typography variant="overline" sx={{ color: 'text.secondary', fontStyle: 'italic', marginBottom: 2 }}>
Sinhala Unicode
</Typography>
<TextField
sx={{
marginBottom: 2
}}
fullWidth
value={translatedTextSi}
onChange={handleChange}
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton onClick={() => onCopy(value)}>
<CopyOutlined />
</IconButton>
</InputAdornment>
)
}}
/> */}
{/* -------- Translated English Unicode ------------------------- */}
{/* <Typography variant="overline" sx={{ color: 'text.secondary', fontStyle: 'italic' }}>
English Unicode
</Typography>
<TextField
fullWidth
value={translatedTextEn}
onChange={handleChange}
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton onClick={() => onCopy(value)}>
<CopyOutlined />
</IconButton>
</InputAdornment>
)
}}
/> */}
</div>
)}
</Stack>
</Box>
</Card>
</Grid>
</Grid>
</Card>
</Box>
</Container>
</ScrollX>
</MainCard>
);
};
export default VideoTranslate;
\ No newline at end of file
// project import
import {
Box,
Button,
ButtonGroup,
Card,
CardContent,
CardHeader,
Container,
Grid,
IconButton,
InputAdornment,
LinearProgress,
Paper,
// Slider,
Stack,
TextField,
Typography
} from '@mui/material';
import MainCard from 'components/MainCard';
import ScrollX from 'components/ScrollX';
import { CloudUploadOutlined, CopyOutlined, HighlightOutlined, TranslationOutlined } from '@ant-design/icons';
import axios from 'axios';
import { MuiFileInput } from 'mui-file-input';
import { useSnackbar } from 'notistack';
import { useState } from 'react';
// ==============================|| List ||============================== //
const VideoTranslate = () => {
const [file, setFile] = useState<File | string | null>(null);
const [videoUrl, setVideoUrl] = useState('');
const [loading, setLoading] = useState(false);
const [value, setValue] = useState('');
const [translatedTextSi, setTranslatedTextSi] = useState('');
const [translatedTextEn, setTranslatedTextEn] = useState('');
const handleDropSingleFile = (files: any) => {
if (files) {
setFile(
Object.assign(files, {
preview: URL.createObjectURL(files)
})
);
setVideoUrl(URL.createObjectURL(files));
}
};
const handleChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
setValue(event.target.value);
};
// ----------------- Video Upload ------------------------------------------------
// const TranslateVideoToSignLanguage = async () => {
// if (file) {
// setLoading(true);
// const formData = new FormData();
// //@ts-ignore
// formData.append('video', file, file.name);
// try {
// const response = await VideoToSignLanguageService.videoTranslation(formData);
// const { translated_text_si, translated_text_en } = response.data;
// setTranslatedTextSi(translated_text_si);
// setTranslatedTextEn(translated_text_en);
// if (response.status == 200) {
// console.log(response.data);
// // setValue(response.data.predictions);
// } else {
// enqueueSnackbar('Something went Wrong!', { variant: 'error' });
// }
// // setLoading(false);
// } catch (error) {
// console.log(error);
// setLoading(false);
// enqueueSnackbar('Something went Wrong!', { variant: 'error' });
// }
// } else {
// enqueueSnackbar('Please select a file.', { variant: 'warning' });
// }
// };
async function uploadVideo() {
setLoading(true)
if (file) {
const formData = new FormData();
formData.append('file', file);
try {
const response = await axios.post('http://127.0.0.1:8000/rest_pyton/uploaded_video', formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
});
setTranslatedTextEn(response.data.translated_text_en)
setTranslatedTextSi(response.data.translated_text_si)
setLoading(false)
} catch (error) {
console.error('Error:', error);
setLoading(false)
}
} else {
console.error('No file selected.');
setLoading(false)
}
}
const { enqueueSnackbar } = useSnackbar();
const onCopy = (text: string) => {
if (text) {
navigator.clipboard.writeText(text);
enqueueSnackbar('Copied!', { variant: 'success' });
}
};
return (
<MainCard content={false}>
<ScrollX>
{/* Content Here */}
<Container
sx={{
padding: 3,
bgcolor: '#625D5D',
color: '#fafafb',
borderRadius: 6,
// boxShadow: '0px 4px 6px rgba(0, 0, 0, 0.1)' // Subtle box shadow
}}
>
{/* Double Button Here */}
<ButtonGroup
disableElevation
variant="contained"
aria-label="Customized buttons"
sx={{
marginBottom: '20px',
backgroundColor: '#ff3c3c', // Change background color
'& .MuiButton-root': { // Apply styles to individual buttons
color: 'white', // Text color
'&:hover': {
backgroundColor: '#000000' // Change color on hover
}
}
}}
>
<Button
sx={{
bgcolor: '#ff3c3c',
padding: '10px 50px',
fontSize: '1.05rem', // Larger font size
'& .anticon': {
fontSize: '1.2rem', // Larger icon size
},
}}
// variant={checkTranalationTypeForUpload()}
startIcon={<CloudUploadOutlined />}
// onClick={() => {
// setIsUploadFile(true);
// }}
>
Upload
</Button>
<Button
sx={{
bgcolor: '#ff3c3c',
padding: '10px 50px',
fontSize: '1.05rem', // Larger font size
'& .anticon': {
fontSize: '1.2rem', // Larger icon size
},
}}
// variant={checkTranalationTypeForRecord()}
startIcon={<HighlightOutlined />}
// onClick={() => {
// setIsUploadFile(false);
// }}
>
Text
</Button>
</ButtonGroup>
{/* Video uploading */}
<Box sx={{ flexGrow: 1 }}>
<Card>
<CardHeader title="Upload a video | Drag & Drop or Select File" />
<Grid container spacing={2}>
<Grid item xs={12} md={6}>
<Card sx={{ marginBottom: '20px', marginLeft: '10px', padding: '35px 10px' }}>
<CardContent>
{/* ! Important */}
{/* @ts-ignore */}
<MuiFileInput value={file} onChange={handleDropSingleFile} inputProps={{ accept: 'video/*' }} />
<Paper style={{ padding: '20px', marginTop: '15px' }}>
<Typography variant="h5" align="center" gutterBottom>
Preview
</Typography>
<div style={{ marginTop: '20px', textAlign: 'center' }}>
{file ? <video src={videoUrl} width="400" controls /> : <p>No Video Selected ...</p>}
</div>
</Paper>
</CardContent>
</Card>
</Grid>
<Grid item xs={12} md={6}>
<Card sx={{ p: 5, minHeight: 300, marginBottom: '10px', marginRight: '10px' }}>
<Box display="grid" gap={5}>
<Stack spacing={2}>
<Grid container spacing={1}>
{/* <Grid item xs={12} md={6}> */}
{/* <h3>Set Sign Speed </h3> */}
{/* <Slider
defaultValue={30}
getAriaValueText={valuetext}
valueLabelDisplay="auto"
step={10}
marks
min={10}
max={110}
/> */}
{/* <h4>Speed - {speed}</h4> */}
{/* </Grid> */}
<Grid item xs={12} md={6} container direction="row" justifyContent="flex-start" alignItems="center">
<Button
variant="contained"
style={{ width: '200px', height: '60px', fontSize: '20px' }}
sx={{
mb: 3
}}
disabled={loading}
onClick={uploadVideo}
endIcon={<TranslationOutlined />}
>
Translate
</Button>
</Grid>
</Grid>
{loading ? (
<Card>
<CardContent>
<LinearProgress />
<center>
<Typography variant="h5" component="div" sx={{ marginTop: 2 }}>
Loading...
</Typography>
</center>
</CardContent>
</Card>
) : (
<div>
{/* -------- Translated Avatar ------------------------- */}
<Typography variant="overline" sx={{ color: 'text.secondary', marginBottom: 2 }}>
Translated Avatar
</Typography>
<Paper elevation={3} sx={{ p: 2, maxWidth: 600, margin: '0 auto', marginBottom: 3 }}>
<video controls width="100%" height="auto">
{/* <source src="your-video-url.mp4" type="video/mp4" /> */}
Your browser does not support the video tag.
</video>
</Paper>
{/* -------- Translated Sinhala Unicode ------------------------- */}
<Typography variant="overline" sx={{ color: 'text.secondary', fontStyle: 'italic', marginBottom: 2 }}>
Sinhala Unicode
</Typography>
<TextField
sx={{
marginBottom: 2
}}
fullWidth
value={translatedTextSi}
onChange={handleChange}
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton onClick={() => onCopy(value)}>
<CopyOutlined />
</IconButton>
</InputAdornment>
)
}}
/>
{/* -------- Translated English Unicode ------------------------- */}
<Typography variant="overline" sx={{ color: 'text.secondary', fontStyle: 'italic' }}>
English Unicode
</Typography>
<TextField
fullWidth
value={translatedTextEn}
onChange={handleChange}
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton onClick={() => onCopy(value)}>
<CopyOutlined />
</IconButton>
</InputAdornment>
)
}}
/>
</div>
)}
</Stack>
</Box>
</Card>
</Grid>
</Grid>
</Card>
</Box>
</Container>
</ScrollX>
</MainCard>
);
};
export default VideoTranslate;
\ No newline at end of file
// import { Alert, Button, Card, CardActions, CardContent, CardHeader, Container, Grid, Typography } from '@mui/material';
// import Head from 'next/head';
// import { useCallback, useState } from 'react';
// import { ComingSoonIllustration } from 'assets/illustrations';
// import CustomBreadcrumbs from 'components/custom-breadcrumbs/CustomBreadcrumbs';
// import Iconify from 'components/iconify/Iconify';
// import HomeWidget from 'sections/@dashboard/spokenLanguageTranslation-module/HomeWidget';
// import _mock from '../../../_mock';
// import Editor from '../../../components/editor';
// import { useSettingsContext } from '../../../components/settings';
// import { Upload } from '../../../components/upload';
// import DashboardLayout from '../../../layout/dashboard';
// import {
// CarouselBasic3
// } from '../../../sections/_examples/extra/carousel';
// SpokenLanguageTranslationHomePage.getLayout = (page: React.ReactElement) => <DashboardLayout>{page}</DashboardLayout>;
// export default function SpokenLanguageTranslationHomePage() {
// const { themeStretch } = useSettingsContext();
// const [translateType, setTranslateType] = useState<"video" | "text">()
// const [file, setFile] = useState<File | string | null>(null);
// const [quillSimple, setQuillSimple] = useState('');
// const handleHomeWidgetOnClick = (value: string) => {
// if (!value) return
// if (value == "Video Translate") {
// setTranslateType("video")
// return
// }
// if (value == "Text Translate") {
// setTranslateType("text")
// return
// }
// }
// const handleDropSingleFile = useCallback((acceptedFiles: File[]) => {
// const file = acceptedFiles[0];
// if (file) {
// setFile(
// Object.assign(file, {
// preview: URL.createObjectURL(file),
// })
// );
// }
// }, []);
// const _carouselsExample = [...Array(5)].map((_, index) => ({
// id: _mock.id(index),
// title: _mock.text.title(index),
// image: _mock.image.cover(index),
// description: _mock.text.description(index),
// }));
// return (
// <>
// <Head>
// <title> Spoken Language Translation Home | SignLink </title>
// </Head>
// <Container maxWidth={themeStretch ? false : 'xl'}>
// <CustomBreadcrumbs
// heading="Spoken Language Translation"
// links={[
// {
// name: 'Dashboard',
// href: '#',
// icon: <Iconify icon="eva:home-fill" />,
// },
// { name: 'Spoken Language Translation Module', href: '#', icon: <Iconify icon="eva:cube-outline" /> },
// { name: 'Spoken Language Translation', icon: <Iconify icon="eva:cube-outline" /> },
// ]}
// />
// </Container>
// <Container maxWidth={themeStretch ? false : 'xl'}>
// <Grid container spacing={2} rowSpacing={3}>
// <Grid item xs={12} md={6} lg={6}>
// <HomeWidget
// title="Video Translate"
// subTitle="Spoken Language Video Convert to sign language"
// icon="eva:video-fill"
// color='warning'
// handleClick={handleHomeWidgetOnClick}
// />
// </Grid>
// <Grid item xs={12} md={6} lg={6}>
// <HomeWidget
// title="Text Translate"
// subTitle="Spoken Language Text Convert to sign language"
// icon="eva:text-fill"
// color="success"
// handleClick={handleHomeWidgetOnClick}
// />
// </Grid>
// {!translateType && <>
// <Grid item xs={12} md={12} lg={12}>
// <Alert severity='info' >Select a Translation type</Alert>
// </Grid>
// </>}
// {translateType == "video" && <>
// <Grid item xs={12} md={8} lg={8}>
// <Card>
// <CardHeader
// title="Upload Video"
// subheader="Upload you're video to convert to sigh language"
// />
// <CardContent>
// <Upload file={file} onDrop={handleDropSingleFile} onDelete={() => setFile(null)} />
// </CardContent>
// <CardActions>
// <Button variant="contained" color='error' fullWidth onClick={() => { setFile(null) }}>
// Reset
// </Button>
// <Button variant="contained" fullWidth>
// Convert
// </Button>
// </CardActions>
// </Card>
// </Grid>
// <Grid item xs={12} md={4} lg={4}>
// <Card>
// <CardHeader title="3D Model" />
// <CardContent>
// <Typography variant="h3" paragraph>
// Coming Soon!
// </Typography>
// <Typography sx={{ color: 'text.secondary' }}>
// We are currently working hard on this page!
// </Typography>
// <ComingSoonIllustration sx={{ my: 1, height: 200 }} />
// </CardContent>
// </Card>
// </Grid>
// </>}
// {translateType == "text" && <>
// <Grid item xs={12} md={8} lg={8}>
// <Card sx={{ height: "100%" }}>
// <CardHeader
// title="Enter text"
// subheader="Enter you're text to convert to sigh language"
// />
// <CardContent>
// <Editor
// simple
// id="simple-editor"
// value={quillSimple}
// onChange={(value) => setQuillSimple(value)}
// />
// </CardContent>
// <CardActions>
// <Button variant="contained" color='error' fullWidth onClick={() => { setQuillSimple('') }}>
// Reset
// </Button>
// <Button variant="contained" fullWidth>
// Convert
// </Button>
// </CardActions>
// </Card>
// </Grid>
// <Grid item xs={12} md={4} lg={4}>
// <Card>
// <CardHeader title="Converted Output" />
// <CardContent>
// <CarouselBasic3 data={_carouselsExample} />
// </CardContent>
// </Card>
// </Grid>
// </>}
// </Grid>
// </Container>
// </>
// );
// }
......@@ -27,7 +27,7 @@ const Dashboard = Loadable(lazy(() => import('pages/home/dashboard')));
const MemberManagementList = Loadable(lazy(() => import('pages/member-management/list/list')));
// render - user management page
const UserManagementList = Loadable(lazy(() => import('pages/user-management/list/list')));
const UserManagementList = Loadable(lazy(() => import('pages/user-management/list/list')));
// render - ssl translate process page
const SSLTranslateProcess = Loadable(lazy(() => import('pages/ssl-translate/process/process')));
......@@ -36,8 +36,9 @@ const SSLTranslateProcess = Loadable(lazy(() => import('pages/ssl-translate/proc
const LearningDashboard = Loadable(lazy(() => import('pages/learning-management/dashboard')));
const LearningCurriculums = Loadable(lazy(() => import('pages/learning-management/learning-curriculums/list/list')));
const LearningCurriculumsSubscribed = Loadable(lazy(() => import('pages/learning-management/learning-curriculums-subscribed/list/list')));
const LearningLeadBoard = Loadable(lazy(() => import('pages/learning-management/learning-lead-board/list/list')));
const LearningFeedBack = Loadable(lazy(() => import('pages/learning-management/learning-feedback/list/list')));
const LearningCurriculumsSubscribedTutorial = Loadable(lazy(() => import('pages/learning-management/learning-curriculums-subscribed/tutorial/tutorial')));
// const LearningLeadBoard = Loadable(lazy(() => import('pages/learning-management/learning-lead-board/list/list')));
// const LearningFeedBack = Loadable(lazy(() => import('pages/learning-management/learning-feedback/list/list')));
// render - parameter curriculum management page
const CurriculumManagementList = Loadable(lazy(() => import('pages/parameter/curriculum-management/list/list')));
......@@ -45,6 +46,8 @@ const CurriculumManagementList = Loadable(lazy(() => import('pages/parameter/cur
// render - parameter tutorial management page
const TutorialManagementList = Loadable(lazy(() => import('pages/parameter/tutorial-management/list/list')));
// render - Video to Sign Language page
const VideoTranslation = Loadable(lazy(() => import('pages/video-to-sign-language/VideoTranslate/VideoTranslate')));
// render - audio-detection page
......@@ -119,6 +122,15 @@ const MainRoutes = {
]
},
{
path: 'video-to-sign-language',
children: [
{
path: 'VideoTranslate',
element: <VideoTranslation />
}
]
},
{
path: 'learning-management',
children: [
......@@ -134,13 +146,27 @@ const MainRoutes = {
path: 'curriculums-subscribed',
element: <LearningCurriculumsSubscribed />
},
{
path: 'curriculums-subscribed-tutorial',
children: [
{
path: ':curriculumIndex', // Parameter for curriculum index
children: [
{
path: ':tutorialIndex', // Parameter for tutorial index
element: <LearningCurriculumsSubscribedTutorial />
}
]
}
]
},
{
path: 'lead-board',
element: <LearningLeadBoard />
element: <MaintenanceUnderConstruction />
},
{
path: 'feedback',
element: <LearningFeedBack />
element: <MaintenanceUnderConstruction />
}
]
},
......
import React, { useEffect } from 'react';
// third party
import { useInView } from 'react-intersection-observer';
import { motion, useAnimation } from 'framer-motion';
// =============================|| LANDING - FADE IN ANIMATION ||============================= //
function Animation({ children, variants }: { children: React.ReactElement; variants: any }) {
const controls = useAnimation();
const [ref, inView] = useInView();
useEffect(() => {
if (inView) {
controls.start('visible');
}
}, [controls, inView]);
return (
<motion.div
ref={ref}
animate={controls}
initial="hidden"
transition={{
x: {
type: 'spring',
stiffness: 150,
damping: 30,
duration: 0.5
},
opacity: { duration: 1 }
}}
variants={variants}
>
{children}
</motion.div>
);
}
export default Animation;
// material-ui
import {
Box,
Grid,
LinearProgress,
Stack,
Typography
} from '@mui/material';
import { useTheme } from '@mui/material/styles';
// project import
import TutorialSection from './TutorialSection';
// types
import MainCard from 'components/MainCard';
import { ThemeDirection } from 'types/config';
import { curriculumTypeUserProgress } from "types/userProgress";
// assets
import { CheckCircleOutlined, UnorderedListOutlined } from '@ant-design/icons';
import Reader from 'assets/images/analytics/reader.svg';
import ReportCard from 'components/cards/statistics/ReportCard';
// ==============================|| Curriculum - Section ||============================== //
const CurriculumSection = ({ curriculum, curriculumIndex }: { curriculum: curriculumTypeUserProgress, curriculumIndex: number }) => {
const theme = useTheme();
return (
<>
<Stack spacing={2} sx={{ padding: 2 }}>
<MainCard title="Overview">
<Grid container spacing={2}>
<Grid
item
xs={7}
sm={7}
sx={{
bgcolor: `${theme.palette.primary.main}`,
position: 'relative',
p: 2.75,
borderRadius: { xs: 2, sm: '8px 0px 0px 8px' },
overflow: 'hidden'
}}
>
<Stack>
<Typography variant="h5" color="white">
{curriculum.curriculumDescription}
</Typography>
<Typography color={theme.palette.grey[0]} variant="caption" sx={{ maxWidth: '55%', pt: 1 }}>
Your learning capacity is 80% as daily analytics
</Typography>
<Typography variant="h4" color="white" sx={{ pt: 8, pb: 1, zIndex: 1 }}>
{(curriculum?.curriculumMarkUser! / curriculum?.curriculumMark!) * 100}% Completed
</Typography>
<Box sx={{ maxWidth: '60%' }}>
<LinearProgress variant="determinate" color="success" value={(curriculum?.curriculumMarkUser! / curriculum?.curriculumMark!) * 100} />
</Box>
<Box
sx={{
position: 'absolute',
bottom: -7,
right: 0,
...(theme.direction === ThemeDirection.RTL && { transform: { xs: 'rotateY(180deg)', sm: 'inherit' } })
}}
>
<img alt="reder" src={Reader} />
</Box>
</Stack>
</Grid>
<Grid md={5} item>
<Grid container spacing={2}>
<Grid md={12} item>
<ReportCard primary={`LEVEL - ${curriculum.curriculumLevel}`} secondary="Curriculum Level" color={theme.palette.error.main} iconPrimary={UnorderedListOutlined} />
</Grid>
<Grid md={12} item>
<ReportCard primary={`Pass Mark - ${curriculum.curriculumMark}`} secondary="Curriculum Pass Mark" color={theme.palette.success.dark} iconPrimary={CheckCircleOutlined} />
</Grid>
</Grid>
</Grid>
</Grid>
</MainCard>
<MainCard title="Tutorials">
<Grid container spacing={2}>
{curriculum.tutorials && curriculum.tutorials?.map((tutorial, index) => {
return (<TutorialSection tutorial={tutorial!} curriculumIndex={curriculumIndex} tutorialIndex={index} />)
})}
</Grid>
</MainCard>
</Stack>
</>
);
};
export default CurriculumSection;
import { useState } from 'react';
import { useNavigate } from 'react-router';
// material-ui
import {
Box,
Button,
Grid,
Typography
} from '@mui/material';
// project import
import Animation from 'sections/learning-management/learning-curriculums-subscribed/Animation';
// types
import { tutorialTypeUserProgress } from "types/userProgress";
// assets
import { PlaySquareOutlined } from '@ant-design/icons';
import AnimateButton from 'components/@extended/AnimateButton';
import MainCard from 'components/MainCard';
// ==============================|| Tutorial - Section ||============================== //
const TutorialSection = ({ tutorial, tutorialIndex, curriculumIndex }: { tutorial: tutorialTypeUserProgress, curriculumIndex: number, tutorialIndex: number }) => {
let navigation = useNavigate()
const [desc, setDesc] = useState(tutorial.tutorialDescription?.slice(0, 100))
const [readMore, setReadMore] = useState(false)
return (
<>
<Grid item md={3}>
<Animation
variants={{
visible: { opacity: 1 },
hidden: { opacity: 0 }
}}
>
<MainCard contentSX={{ p: 3 }}>
<Grid container spacing={1.5}>
<Grid item xs={12}>
<Typography variant="h4" sx={{ fontWeight: 600, mt: 2 }}>
{tutorial.tutorialTitle}
</Typography>
</Grid>
<Grid item xs={12}>
<Typography variant="body1" color="secondary" sx={{ textAlign: "justify" }}>
{desc}
<span style={{ fontWeight: "bold", cursor: "pointer" }}
onClick={() => {
if (!readMore) {
setDesc(tutorial.tutorialDescription)
setReadMore(true)
} else {
setDesc(tutorial.tutorialDescription?.slice(0, 100))
setReadMore(false)
}
}} color="secondary">
{readMore ? "Show Less" : "...Read More"}
</span>
</Typography>
</Grid>
<Grid item xs={12}>
<Box sx={{ display: 'inline-block' }}>
<AnimateButton>
<Button
variant="outlined"
endIcon={<PlaySquareOutlined />}
sx={{ my: 2 }}
onClick={() => { navigation(`/learning-management/curriculums-subscribed-tutorial/${curriculumIndex}/${tutorialIndex}`) }}
>
Start Tutorial
</Button>
</AnimateButton>
</Box>
</Grid>
<Grid item xs={12} sx={{ '& img': { mb: -3.75, width: `calc( 100% + 24px)` } }}>
<img src={tutorial.tutorialImage} alt="feature" />
</Grid>
</Grid>
</MainCard>
</Animation>
</Grid>
</>
)
}
export default TutorialSection;
\ No newline at end of file
// material-ui
import { CardContent, Grid, Skeleton, Stack, Avatar } from '@mui/material';
// project import
import MainCard from 'components/MainCard';
// assets
import { ContactsOutlined } from '@ant-design/icons';
// ===========================|| SKELETON - USER EMPTY CARD ||=========================== //
const UserCard = () => {
return (
<MainCard
border={false}
content={false}
boxShadow
sx={{ boxShadow: `rgba(50, 50, 93, 0.25) 0px 13px 27px -5px, rgba(0, 0, 0, 0.3) 0px 8px 16px -8px`, borderRadius: 2 }}
>
<CardContent sx={{ p: 2 }}>
<Grid container spacing={2}>
<Grid item xs={12}>
<Stack flexDirection="row" alignItems="center">
<Avatar>
<ContactsOutlined style={{ visibility: 'inherit' }} />
</Avatar>
<Stack sx={{ width: '100%', pl: 2.5 }}>
<Skeleton animation={false} height={20} width="80%" />
<Skeleton animation={false} height={20} width="40%" />
</Stack>
</Stack>
</Grid>
<Grid item xs={12}>
<Skeleton animation={false} height={20} width={45} />
<Skeleton animation={false} height={20} />
<Stack direction="row" alignItems="center" spacing={1}>
<Skeleton animation={false} height={20} width={90} />
<Skeleton animation={false} height={20} width={38} />
</Stack>
</Grid>
<Grid item xs={12}>
<Stack direction="row" justifyContent="space-between" alignItems="center">
<Grid container spacing={1}>
<Grid item>
<Skeleton animation={false} height={20} width={40} />
</Grid>
<Grid item>
<Skeleton animation={false} height={17} width={20} />
</Grid>
</Grid>
<Skeleton animation={false} height={32} width={47} />
</Stack>
</Grid>
</Grid>
</CardContent>
</MainCard>
);
};
export default UserCard;
// material-ui
import { Box, Grid, Stack, Typography } from '@mui/material';
// project import
import CurriculumCard from './CurriculumCard';
interface Props {
title: string;
}
// ==============================|| EMPTY STATE ||============================== //
const EmptyCurriculumCard = ({ title }: Props) => {
return (
<Grid container spacing={3}>
<Grid item xs={12}>
<Box
sx={{
p: { xs: 2.5, sm: 6 },
height: `calc(100vh - 192px)`,
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
bgcolor: 'transparent'
}}
>
<Grid container direction="column" justifyContent="center" alignItems="center">
<Grid item>
<Box sx={{ ml: -9, mb: { xs: -8, sm: -5 } }}>
<Box sx={{ position: 'relative' }}>
<CurriculumCard />
</Box>
<Box sx={{ position: 'relative', top: -120, left: 72 }}>
<CurriculumCard />
</Box>
</Box>
</Grid>
<Grid item>
<Stack spacing={1}>
<Typography align="center" variant="h4">
{title}
</Typography>
</Stack>
</Grid>
</Grid>
</Box>
</Grid>
</Grid>
);
};
export default EmptyCurriculumCard;
import React, { useEffect } from 'react';
// third party
import { useInView } from 'react-intersection-observer';
import { motion, useAnimation } from 'framer-motion';
// =============================|| LANDING - FADE IN ANIMATION ||============================= //
function Animation({ children, variants }: { children: React.ReactElement; variants: any }) {
const controls = useAnimation();
const [ref, inView] = useInView();
useEffect(() => {
if (inView) {
controls.start('visible');
}
}, [controls, inView]);
return (
<motion.div
ref={ref}
animate={controls}
initial="hidden"
transition={{
x: {
type: 'spring',
stiffness: 150,
damping: 30,
duration: 0.5
},
opacity: { duration: 1 }
}}
variants={variants}
>
{children}
</motion.div>
);
}
export default Animation;
import { useState } from 'react';
import { useEffect, useState } from 'react';
// material-ui
import {
Box,
Button,
Divider,
Fade,
CircularProgress,
Grid,
List,
ListItem,
ListItemAvatar,
ListItemText,
Menu,
MenuItem,
Stack,
Typography
} from '@mui/material';
// third-party
import { PDFDownloadLink } from '@react-pdf/renderer';
// project import
import IconButton from 'components/@extended/IconButton';
import MainCard from 'components/MainCard';
// assets
import { MoreOutlined } from '@ant-design/icons';
import Avatar from 'components/@extended/Avatar';
import curriculumLevels from 'data/curriculumLevels';
// types
export interface curriculumCardProps {
_id: number | string | undefined;
curriculumCode: string;
curriculumLevel: number;
curriculumName: string;
curriculumDescription: string;
curriculumImage: string;
tutorials?: tutorialItemProps[];
status?: number;
createdBy?: string;
updatedBy?: string;
createdAt?: Date;
updatedAt?: Date;
}
export interface tutorialItemProps {
_id: number | string | undefined;
tutorialCode: string;
tutorialTitle: string;
tutorialDescription: string;
tutorialImage: string;
status?: number;
createdBy?: string;
updatedBy?: string;
createdAt?: Date;
updatedAt?: Date;
taskItems?: taskItemProps[]
}
export interface taskItemProps {
_id: number | string | undefined;
title: string;
description: string;
howToDo: string;
referenceImage: string;
referenceVideo: string;
}
import { MinusOutlined, PlusOutlined, SendOutlined } from '@ant-design/icons';
import AnimateButton from 'components/@extended/AnimateButton';
import useAuth from 'hooks/useAuth';
import { useDispatch, useSelector } from 'store';
import { openSnackbar } from 'store/reducers/snackbar';
import { addUserProgress, toInitialState } from 'store/reducers/userProgress';
import { curriculumType } from 'types/curriculum';
import Animation from './Animation';
import CurriculumPreview from './CurriculumPreview';
// ==============================|| CURRICULUM - CARD ||============================== //
// types
const CurriculumCard = ({ curriculum }: { curriculum: curriculumCardProps }) => {
// const [open, setOpen] = useState(false);
// ==============================|| CURRICULUM - CARD ||============================== //
// const handleClickOpen = () => {
// setOpen(true);
// };
const CurriculumCard = ({ curriculum }: { curriculum: curriculumType }) => {
let dispatch = useDispatch()
const { error, success, isLoading } = useSelector(state => state.userProgress);
const { user } = useAuth();
// const handleClose = () => {
// setOpen(false);
// };
const [open, setOpen] = useState(false);
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const openMenu = Boolean(anchorEl);
const handleMenuClick = (event: React.MouseEvent<HTMLElement>) => {
setAnchorEl(event.currentTarget);
const handleClickOpen = () => {
setOpen(true);
};
const handleMenuClose = () => {
setAnchorEl(null);
const handleClose = () => {
setOpen(false);
};
const [desc, setDesc] = useState(curriculum.curriculumDescription?.slice(0, 100))
const [readMore, setReadMore] = useState(false)
const [isSubscribed, setIsSubscribed] = useState(false)
/**
* API Config
* User Progress API
*/
const FollowCurriculum = () => {
if (!user) {
// User is missing
dispatch(
openSnackbar({
open: true,
message: 'User data is missing',
variant: 'alert',
alert: {
color: 'warning',
},
close: true,
})
);
} else if (!user.id) {
// User ID is missing
dispatch(
openSnackbar({
open: true,
message: 'User ID is missing',
variant: 'alert',
alert: {
color: 'warning',
},
close: true,
})
);
} else if (!curriculum) {
// Curriculum data is missing
dispatch(
openSnackbar({
open: true,
message: 'Curriculum data is missing',
variant: 'alert',
alert: {
color: 'warning',
},
close: true,
})
);
} else {
// All required data is present, dispatch addUserProgress
dispatch(addUserProgress(user.id, curriculum));
}
}
// handel error
useEffect(() => {
if (error != null) {
dispatch(
openSnackbar({
open: true,
//@ts-ignore
message: error ? error.Message : "Something went wrong ...",
variant: 'alert',
alert: {
color: 'error'
},
close: true
})
);
dispatch(toInitialState())
}
}, [error])
// handel success
useEffect(() => {
if (success != null) {
dispatch(
openSnackbar({
open: true,
message: success,
variant: 'alert',
alert: {
color: 'success'
},
close: true
})
);
dispatch(toInitialState())
}
}, [success])
useEffect(() => {
if (curriculum.subscribedUser) {
const userId = user?.id; // Assuming user.id is how you get the user's ID
const isUserSubscribed = curriculum.subscribedUser.includes(userId!);
if (isUserSubscribed) {
setIsSubscribed(true)
}
}
}, [curriculum, user]);
return (
<>
<MainCard sx={{ height: 1, '& .MuiCardContent-root': { height: 1, display: 'flex', flexDirection: 'column' } }}>
<Grid id="print" container spacing={2.25}>
<Grid item xs={12}>
<List sx={{ width: 1, p: 0 }}>
<ListItem
disablePadding
secondaryAction={
<IconButton edge="end" aria-label="comments" color="secondary" onClick={handleMenuClick}>
<MoreOutlined style={{ fontSize: '1.15rem' }} />
</IconButton>
}
>
<ListItemAvatar>
<Avatar alt={curriculum.curriculumName!} src={curriculum.curriculumImage!} />
</ListItemAvatar>
<ListItemText
primary={<Typography variant="subtitle1">{curriculum.curriculumName}</Typography>}
secondary={
<Typography variant="caption" color="secondary">
{curriculumLevels.find(level => level.id === curriculum.curriculumLevel)?.description || ""}
</Typography>
}
/>
</ListItem>
</List>
<Menu
id="fade-menu"
MenuListProps={{
'aria-labelledby': 'fade-button'
}}
anchorEl={anchorEl}
open={openMenu}
onClose={handleMenuClose}
TransitionComponent={Fade}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'right'
}}
transformOrigin={{
vertical: 'top',
horizontal: 'right'
}}
>
<MenuItem sx={{ a: { textDecoration: 'none', color: 'inherit' } }}>
<PDFDownloadLink
document={<></>} fileName={`${curriculum.curriculumCode}-${curriculum.curriculumName}.pdf`}
// document={<ListSmallCard customer={customer} />} fileName={`Customer-${customer.fatherName}.pdf`}
>
Export PDF
</PDFDownloadLink>
</MenuItem>
</Menu>
</Grid>
<Grid item xs={12}>
<Divider />
</Grid>
<Grid item xs={12}>
<Typography>{curriculum.curriculumDescription}</Typography>
</Grid>
{/* <Grid item xs={12}>
<Grid container spacing={1}>
<Grid item xs={6}>
<List sx={{ p: 0, overflow: 'hidden', '& .MuiListItem-root': { px: 0, py: 0.5 } }}>
<ListItem>
<ListItemIcon>
<MailOutlined />
</ListItemIcon>
<ListItemText primary={<Typography color="secondary">{customer.email}</Typography>} />
</ListItem>
<ListItem>
<ListItemIcon>
<PhoneOutlined />
</ListItemIcon>
<ListItemText
primary={
<Typography color="secondary">
<PatternFormat displayType="text" format="+1 (###) ###-####" mask="_" defaultValue={customer.contact} />
</Typography>
}
/>
</ListItem>
</List>
</Grid>
<Grid item xs={6}>
<List sx={{ p: 0, overflow: 'hidden', '& .MuiListItem-root': { px: 0, py: 0.5 } }}>
<ListItem>
<ListItemIcon>
<EnvironmentOutlined />
</ListItemIcon>
<ListItemText primary={<Typography color="secondary">{customer.country}</Typography>} />
</ListItem>
<ListItem>
<ListItemIcon>
<LinkOutlined />
</ListItemIcon>
<ListItemText
primary={
<Link href="https://google.com" target="_blank" sx={{ textTransform: 'lowercase' }}>
https://{customer.firstName}.en
</Link>
<Animation
variants={{
visible: { opacity: 1 },
hidden: { opacity: 0 }
}}
>
<MainCard contentSX={{ p: 3 }}>
<Grid container spacing={1.5}>
<Grid item xs={12}>
<Typography variant="h3" sx={{ fontWeight: 600, mt: 2 }}>
{curriculum.curriculumTitle}
</Typography>
</Grid>
<Grid item xs={12}>
<Typography variant="body1" color="secondary" sx={{ textAlign: "justify" }}>
{desc}
<span style={{ fontWeight: "bold", cursor: "pointer" }}
onClick={() => {
if (!readMore) {
setDesc(curriculum.curriculumDescription)
setReadMore(true)
} else {
setDesc(curriculum.curriculumDescription?.slice(0, 100))
setReadMore(false)
}
}} color="secondary">
{readMore ? "Show Less" : "...Read More"}
</span>
</Typography>
</Grid>
<Grid item xs={6}>
<Box sx={{ display: 'inline-block', width: "100%" }}>
<AnimateButton>
<Button
fullWidth
size='small'
variant="outlined"
endIcon={isSubscribed ? <MinusOutlined /> : <PlusOutlined />}
sx={{ my: 2, width: "100%" }}
onClick={() => {
if (!isSubscribed) {
FollowCurriculum()
}
/>
</ListItem>
</List>
</Grid>
}}
color={isSubscribed ? 'secondary' : 'success'}
disabled={isLoading}
>
{isLoading ? <CircularProgress /> : <>{isSubscribed ? 'Un Follow Curriculum' : 'Follow Curriculum'} </>}
</Button>
</AnimateButton>
</Box>
</Grid>
</Grid>
<Grid item xs={12}>
<Box>
<Box
sx={{
display: 'flex',
flexWrap: 'wrap',
listStyle: 'none',
p: 0.5,
m: 0
}}
component="ul"
>
{customer.skills.map((skill: string, index: number) => (
<ListItem disablePadding key={index} sx={{ width: 'auto', pr: 0.75, pb: 0.75 }}>
<Chip color="secondary" variant="outlined" size="small" label={skill} />
</ListItem>
))}
<Grid item xs={6}>
<Box sx={{ display: 'inline-block', width: "100%" }}>
<AnimateButton>
<Button
fullWidth
size='small'
variant="outlined"
endIcon={<SendOutlined />}
sx={{ my: 2, width: "100%" }}
onClick={handleClickOpen}
>
Preview Curriculum
</Button>
</AnimateButton>
</Box>
</Box>
</Grid> */}
</Grid>
<Stack
direction="row"
className="hideforPDf"
alignItems="center"
spacing={1}
justifyContent="space-between"
sx={{ mt: 'auto', mb: 0, pt: 2.25 }}
>
<Typography variant="caption" color="secondary">
Updated in {curriculum.createdAt?.toLocaleTimeString()}
</Typography>
<Button variant="outlined" size="small" onClick={() => {
// handleClickOpen()
}}>
Preview
</Button>
</Stack>
</MainCard>
{/* edit customer dialog */}
{/* <CustomerPreview customer={customer} open={open} onClose={handleClose} /> */}
</Grid>
<Grid item xs={12} sx={{ '& img': { marginBottom: '-3.75px', width: '100%', height: '456px', objectFit: 'cover' } }}>
<img src={curriculum.curriculumImage} alt="feature" />
</Grid>
</Grid>
</MainCard>
</Animation>
{/* edit curriculum dialog */}
<CurriculumPreview curriculum={curriculum} open={open} onClose={handleClose} />
</>
);
};
......
import { useState } from 'react';
// material-ui
import {
Accordion, AccordionDetails, AccordionSummary,
Box,
Button,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
Grid,
List,
ListItem,
ListItemAvatar,
ListItemText,
Stack,
Tooltip,
Typography
} from '@mui/material';
import { useTheme } from '@mui/material/styles';
// third-party
import { PDFDownloadLink } from '@react-pdf/renderer';
// project import
import Avatar from 'components/@extended/Avatar';
import IconButton from 'components/@extended/IconButton';
import { PopupTransition } from 'components/@extended/Transitions';
import MainCard from 'components/MainCard';
import SimpleBar from 'components/third-party/SimpleBar';
// assets
import { DownloadOutlined, TagOutlined } from '@ant-design/icons';
import curriculumLevels from 'data/curriculumLevels';
import { curriculumType } from 'types/curriculum';
// types
// ==============================|| Curriculum - CARD PREVIEW ||============================== //
export default function CurriculumPreview({ curriculum, open, onClose }: { curriculum: curriculumType; open: boolean; onClose: () => void }) {
const theme = useTheme();
const [expanded, setExpanded] = useState<string | false>('panel0');
const handleChange = (panel: string) => (event: React.SyntheticEvent, newExpanded: boolean) => {
setExpanded(newExpanded ? panel : false);
};
return (
<>
<Dialog
open={open}
TransitionComponent={PopupTransition}
keepMounted
onClose={onClose}
aria-describedby="alert-dialog-slide-description"
sx={{ '& .MuiDialog-paper': { width: 1024, maxWidth: 1, m: { xs: 1.75, sm: 2.5, md: 4 } } }}
>
<Box id="PopupPrint" sx={{ px: { xs: 2, sm: 3, md: 5 }, py: 1 }}>
<DialogTitle sx={{ px: 0 }}>
<List sx={{ width: 1, p: 0 }}>
<ListItem
disablePadding
secondaryAction={
<Stack direction="row" alignItems="center" justifyContent="center" spacing={0}>
<Tooltip title="Export">
<PDFDownloadLink
document={<></>} fileName={`${curriculum.curriculumCode}-${curriculum.curriculumTitle}.pdf`}
// document={<ListCard customer={customer} />} fileName={`Customer-${customer.fatherName}.pdf`}
>
<IconButton color="secondary">
<DownloadOutlined />
</IconButton>
</PDFDownloadLink>
</Tooltip>
{/* <Tooltip title="Edit">
<IconButton color="secondary" onClick={handleAdd}>
<EditOutlined />
</IconButton>
</Tooltip>
<Tooltip title="Delete" onClick={handleClose}>
<IconButton color="error">
<DeleteOutlined />
</IconButton>
</Tooltip> */}
</Stack>
}
>
<ListItemAvatar sx={{ mr: 0.75 }}>
<Avatar alt={curriculum.curriculumTitle} size="lg" src={curriculum.curriculumImage} />
</ListItemAvatar>
<ListItemText
primary={<Typography variant="h5">{curriculum.curriculumTitle}</Typography>}
secondary={<Typography color="secondary"> {curriculumLevels.find(level => level.id === curriculum.curriculumLevel)?.description || ""}</Typography>}
/>
</ListItem>
</List>
</DialogTitle>
<DialogContent dividers sx={{ px: 0 }}>
<SimpleBar sx={{ height: 'calc(100vh - 390px)' }}>
<Grid container spacing={3}>
<Grid item xs={12} sm={8} xl={12}>
<Grid container spacing={2.25}>
<Grid item xs={12}>
<MainCard title="Overview">
<Typography>
{curriculum.curriculumDescription}
</Typography>
</MainCard>
</Grid>
<Grid item xs={12}>
<MainCard title="Tutorials">
<Box
sx={{
'& .MuiAccordion-root': {
borderColor: theme.palette.divider,
'& .MuiAccordionSummary-root': {
bgcolor: 'transparent',
flexDirection: 'row',
'&:focus-visible': {
bgcolor: 'primary.lighter'
}
},
'& .MuiAccordionDetails-root': {
borderColor: theme.palette.divider
},
'& .Mui-expanded': {
color: theme.palette.primary.main
}
}
}}
>
{curriculum.tutorials?.map((tutorial, index) => {
return (
<>
<Accordion expanded={expanded === `panel${index}`} onChange={handleChange(`panel${index}`)}>
<AccordionSummary aria-controls={`panel${index}d-content`} id={`panel${index}d-header`}>
<Stack direction="row" spacing={1.5} alignItems="center">
<TagOutlined />
<Typography variant="h6">{tutorial.tutorialTitle}</Typography>
</Stack>
</AccordionSummary>
<AccordionDetails>
<Stack spacing={2}>
<Typography variant="h5">{tutorial.tutorialDescription}</Typography>
</Stack>
</AccordionDetails>
</Accordion>
</>
)
})}
</Box>
</MainCard>
</Grid>
</Grid>
</Grid>
</Grid>
</SimpleBar>
</DialogContent>
<DialogActions>
<Button color="error" onClick={onClose}>
Close
</Button>
</DialogActions>
</Box>
</Dialog>
</>
);
}
import { useState } from 'react';
import { useEffect, useState } from 'react';
// material-ui
import {
......@@ -33,6 +33,12 @@ import IconButton from 'components/@extended/IconButton';
import { DeleteFilled } from '@ant-design/icons';
import MainCard from 'components/MainCard';
import curriculumLevels, { CurriculumLevelsType } from 'data/curriculumLevels';
import useAuth from 'hooks/useAuth';
import { dispatch, useSelector } from 'store';
import { addCurriculum } from 'store/reducers/curriculum';
import { fetchTutorials } from 'store/reducers/tutorial';
import { Curriculum } from 'types/curriculum';
import { Tutorial } from 'types/tutorial';
import AlertCurriculumDelete from './AlertCurriculumDelete';
// types
......@@ -60,25 +66,13 @@ const getInitialValues = (curriculum: FormikValues | null) => {
// ==============================|| CUSTOMER ADD / EDIT ||============================== //
export interface Props {
curriculum?: {
_id: number | string | undefined;
curriculumCode: string;
curriculumLevel: number;
curriculumTitle: string;
curriculumDescription: string;
curriculumImage: string;
tutorials: string[];
status: number;
createdBy: string;
updatedBy: string;
createdAt: Date;
updatedAt: Date;
};
curriculum: Curriculum;
onCancel: () => void;
}
const AddEditCurriculum = ({ curriculum, onCancel }: Props) => {
const theme = useTheme();
const { user } = useAuth();
const isCreating = !curriculum;
......@@ -95,12 +89,27 @@ const AddEditCurriculum = ({ curriculum, onCancel }: Props) => {
initialValues: getInitialValues(curriculum!),
validationSchema: CurriculumSchema,
enableReinitialize: true,
onSubmit: (values, { setSubmitting, resetForm }) => {
onSubmit: (values, { setSubmitting, resetForm }) => {
try {
if (curriculum) {
// PUT API
} else {
// POST API
// const dummyObject: Curriculum = curriculumReqOb
// dispatch(addCurriculum(dummyObject))
const req: Curriculum = {
curriculumCode: values.curriculumCode,
curriculumLevel: values.curriculumLevel!,
curriculumTitle: values.curriculumTitle,
curriculumDescription: values.curriculumDescription,
curriculumImage: values.curriculumImage,
tutorials: values.tutorials!,
createdBy: user?.name!,
};
dispatch(addCurriculum(req))
}
resetForm()
setSubmitting(false);
......@@ -114,6 +123,16 @@ const AddEditCurriculum = ({ curriculum, onCancel }: Props) => {
const { errors, touched, handleSubmit, isSubmitting, getFieldProps, values } = formik;
// const { handleSubmit, isSubmitting } = formik;
const { tutorials } = useSelector(state => state.tutorial);
/**
* API Config
* Tutorial API
*/
useEffect(() => {
dispatch(fetchTutorials());
}, [dispatch])
return (
<>
<FormikProvider value={formik}>
......@@ -232,18 +251,18 @@ const AddEditCurriculum = ({ curriculum, onCancel }: Props) => {
values.tutorials.map((item, index) => {
return (
<>
<Grid item xs={11}>
<Grid item xs={11}>
<Stack spacing={1.25}>
<InputLabel htmlFor={`tutorials.${index}`}>Tutorial</InputLabel>
<Autocomplete
fullWidth
id={`tutorials.${index}`}
// value={tutorials.find((option) => option.id === formik.values.curriculumLevel) || null}
// onChange={(event: any, newValue: CurriculumLevelsType | null) => {
// formik.setFieldValue(`tutorials.${index}`, newValue?.id);
// }}
options={[]}
// getOptionLabel={(item) => `${item.description}`}
value={tutorials.find((option) => option._id === formik.values.tutorials[index]) || null}
onChange={(event: any, newValue: Tutorial | null) => {
formik.setFieldValue(`tutorials.${index}`, newValue?._id);
}}
options={tutorials}
getOptionLabel={(item) => `${item.tutorialTitle}`}
renderInput={(params) => {
return (
<TextField
......
......@@ -7,20 +7,22 @@ import { PopupTransition } from 'components/@extended/Transitions';
// assets
import { DeleteFilled } from '@ant-design/icons';
import { useDispatch } from 'store';
import { deleteCurriculum } from 'store/reducers/curriculum';
// types
interface Props {
title: string;
open: boolean;
handleClose: (status: boolean) => void;
deleteId: number | string | undefined;
deleteId: string | undefined;
}
// ==============================|| Curriculum - DELETE ||============================== //
export default function AlertCurriculumDelete({ title, open, handleClose, deleteId }: Props) {
// const dispatch = useDispatch();
const dispatch = useDispatch();
return (
<Dialog
......@@ -41,6 +43,14 @@ export default function AlertCurriculumDelete({ title, open, handleClose, delete
<Typography variant="h4" align="center">
Are you sure you want to delete?
</Typography>
<Typography align="center">
By deleting
<Typography variant="subtitle1" component="span">
{' '}
"{title}"{' '}
</Typography>
Curriculum, Its details & tasks will also be deleted.
</Typography>
</Stack>
<Stack direction="row" spacing={2} sx={{ width: 1 }}>
......@@ -48,7 +58,7 @@ export default function AlertCurriculumDelete({ title, open, handleClose, delete
Cancel
</Button>
<Button fullWidth color="error" variant="contained" onClick={() => {
// dispatch(deleteNutrition(deleteId!))
dispatch(deleteCurriculum(deleteId!))
handleClose(true)
}} autoFocus>
Delete
......
......@@ -32,6 +32,10 @@ import IconButton from 'components/@extended/IconButton';
import { DeleteFilled } from '@ant-design/icons';
import { AccordionSummary, InputLabel, TextField } from '@mui/material';
import MainCard from 'components/MainCard';
import useAuth from 'hooks/useAuth';
import { dispatch } from 'store';
import { addTutorial } from 'store/reducers/tutorial';
import { Tutorial } from 'types/tutorial';
import AlertTutorialDelete from './AlertTutorialDelete';
// types
......@@ -53,6 +57,7 @@ const getInitialValues = (tutorial: FormikValues | null) => {
howToDo: "",
referenceImage: "",
referenceVideo: "",
taskItemMark: 0,
}
]
}
......@@ -67,37 +72,37 @@ const getInitialValues = (tutorial: FormikValues | null) => {
// ==============================|| CUSTOMER ADD / EDIT ||============================== //
export interface Props {
tutorial?: {
_id: number | string | undefined
tutorialCode: string;
tutorialTitle: string;
tutorialDescription: string;
tutorialImage: string;
status: number;
createdBy: string;
updatedBy: string;
createdAt: Date;
updatedAt: Date;
taskItems: taskItemProps[]
};
tutorial?: Tutorial
onCancel: () => void;
}
export interface taskItemProps {
_id: number | string | undefined;
title: string;
description: string;
howToDo: string;
referenceImage: string;
referenceVideo: string;
}
const AddEditTutorial = ({ tutorial, onCancel }: Props) => {
const theme = useTheme();
const { user } = useAuth();
const isCreating = !tutorial;
const TutorialSchema = Yup.object().shape({});
const TutorialSchema = Yup.object().shape({
tutorialCode: Yup.string().required(`Tutorial Code is required`),
tutorialTitle: Yup.string().required(`Tutorial Title is required`),
tutorialDescription: Yup.string().required(`Tutorial Description is required`),
tutorialImage: Yup.string().required(`Tutorial Image Ref is required`),
taskItems: Yup.array().of(
Yup.object().shape({
title: Yup.string().required(`Task Title is required`),
description: Yup.string().required(`Task Description is required`),
howToDo: Yup.string().required(`Task How To Do is required`),
referenceImage: Yup.string().required(`Task Reference Image is required`),
referenceVideo: Yup.string(),
taskItemMark: Yup.number().required(`Task Mark is required`).min(1, 'Task Mark must be greater than 0'),
})
).required(`Tutorial Task Item is required`),
status: Yup.number(),
createdBy: Yup.string(),
createdAt: Yup.date(),
updatedBy: Yup.string(),
updatedAt: Yup.date(),
});
const [openAlert, setOpenAlert] = useState(false);
......@@ -115,7 +120,23 @@ const AddEditTutorial = ({ tutorial, onCancel }: Props) => {
if (tutorial) {
// PUT API
} else {
// POST API
// POST API
// const dummyObject: Tutorial = tutorialReqOb
// dispatch(addTutorial(dummyObject))
const req: Tutorial = {
tutorialCode: values.tutorialCode,
tutorialTitle: values.tutorialTitle,
tutorialImage: values.tutorialImage,
tutorialDescription: values.tutorialDescription,
taskItems: values.taskItems.map((item) => ({
...item,
howToDo: item.howToDo.split(','), // Split the comma-separated string into an array
})),
createdBy: user?.name!,
};
dispatch(addTutorial(req))
}
resetForm()
setSubmitting(false);
......@@ -266,7 +287,7 @@ const AddEditTutorial = ({ tutorial, onCancel }: Props) => {
</AccordionSummary>
<AccordionDetails>
<Grid container spacing={2}>
<Grid item xs={12}>
<Grid item xs={6}>
<Stack spacing={1.25}>
<InputLabel htmlFor={`taskItems.${index}.title`}>Task Title</InputLabel>
<TextField
......@@ -274,8 +295,37 @@ const AddEditTutorial = ({ tutorial, onCancel }: Props) => {
id={`taskItems.${index}.title`}
placeholder="Enter Task Title"
{...getFieldProps(`taskItems.${index}.title`)}
// error={Boolean(touched.ingredientNutritions![index].nutritionCategory && errors.ingredientNutritions![index].nutritionCategory)}
// helperText={touched.ingredientNutritions![index].nutritionCategory && errors.ingredientNutritions![index].nutritionCategory}
error={Boolean(
touched.taskItems && touched.taskItems[index] &&
//@ts-ignore
errors.taskItems && errors.taskItems[index] && errors.taskItems[index].title
)}
helperText={
touched.taskItems && touched.taskItems[index] &&
//@ts-ignore
errors.taskItems && errors.taskItems[index] && errors.taskItems[index].title
}
/>
</Stack>
</Grid>
<Grid item xs={6}>
<Stack spacing={1.25}>
<InputLabel htmlFor={`taskItems.${index}.taskItemMark`}>Task Mark</InputLabel>
<TextField
fullWidth
id={`taskItems.${index}.taskItemMark`}
placeholder="Enter Task Mark"
{...getFieldProps(`taskItems.${index}.taskItemMark`)}
error={Boolean(
touched.taskItems && touched.taskItems[index] &&
//@ts-ignore
errors.taskItems && errors.taskItems[index] && errors.taskItems[index].taskItemMark
)}
helperText={
touched.taskItems && touched.taskItems[index] &&
//@ts-ignore
errors.taskItems && errors.taskItems[index] && errors.taskItems[index].taskItemMark
}
/>
</Stack>
</Grid>
......@@ -287,8 +337,16 @@ const AddEditTutorial = ({ tutorial, onCancel }: Props) => {
id={`taskItems.${index}.referenceImage`}
placeholder="Enter Task Reference Image"
{...getFieldProps(`taskItems.${index}.referenceImage`)}
// error={Boolean(touched.ingredientNutritions![index].nutritionCategory && errors.ingredientNutritions![index].nutritionCategory)}
// helperText={touched.ingredientNutritions![index].nutritionCategory && errors.ingredientNutritions![index].nutritionCategory}
error={Boolean(
touched.taskItems && touched.taskItems[index] &&
//@ts-ignore
errors.taskItems && errors.taskItems[index] && errors.taskItems[index].referenceImage
)}
helperText={
touched.taskItems && touched.taskItems[index] &&
//@ts-ignore
errors.taskItems && errors.taskItems[index] && errors.taskItems[index].referenceImage
}
/>
</Stack>
</Grid>
......@@ -300,8 +358,16 @@ const AddEditTutorial = ({ tutorial, onCancel }: Props) => {
id={`taskItems.${index}.referenceVideo`}
placeholder="Enter Task Reference Video"
{...getFieldProps(`taskItems.${index}.referenceVideo`)}
// error={Boolean(touched.ingredientNutritions![index].nutritionCategory && errors.ingredientNutritions![index].nutritionCategory)}
// helperText={touched.ingredientNutritions![index].nutritionCategory && errors.ingredientNutritions![index].nutritionCategory}
error={Boolean(
touched.taskItems && touched.taskItems[index] &&
//@ts-ignore
errors.taskItems && errors.taskItems[index] && errors.taskItems[index].referenceVideo
)}
helperText={
touched.taskItems && touched.taskItems[index] &&
//@ts-ignore
errors.taskItems && errors.taskItems[index] && errors.taskItems[index].referenceVideo
}
/>
</Stack>
</Grid>
......@@ -313,8 +379,16 @@ const AddEditTutorial = ({ tutorial, onCancel }: Props) => {
id={`taskItems.${index}.description`}
placeholder="Enter Task Description"
{...getFieldProps(`taskItems.${index}.description`)}
// error={Boolean(touched.ingredientNutritions![index].nutritionCategory && errors.ingredientNutritions![index].nutritionCategory)}
// helperText={touched.ingredientNutritions![index].nutritionCategory && errors.ingredientNutritions![index].nutritionCategory}
error={Boolean(
touched.taskItems && touched.taskItems[index] &&
//@ts-ignore
errors.taskItems && errors.taskItems[index] && errors.taskItems[index].description
)}
helperText={
touched.taskItems && touched.taskItems[index] &&
//@ts-ignore
errors.taskItems && errors.taskItems[index] && errors.taskItems[index].description
}
/>
</Stack>
</Grid>
......@@ -326,8 +400,16 @@ const AddEditTutorial = ({ tutorial, onCancel }: Props) => {
id={`taskItems.${index}.howToDo`}
placeholder="Enter Task How To Do"
{...getFieldProps(`taskItems.${index}.howToDo`)}
// error={Boolean(touched.ingredientNutritions![index].nutritionCategory && errors.ingredientNutritions![index].nutritionCategory)}
// helperText={touched.ingredientNutritions![index].nutritionCategory && errors.ingredientNutritions![index].nutritionCategory}
error={Boolean(
touched.taskItems && touched.taskItems[index] &&
//@ts-ignore
errors.taskItems && errors.taskItems[index] && errors.taskItems[index].howToDo
)}
helperText={
touched.taskItems && touched.taskItems[index] &&
//@ts-ignore
errors.taskItems && errors.taskItems[index] && errors.taskItems[index].howToDo
}
/>
</Stack>
</Grid>
......
......@@ -7,20 +7,22 @@ import { PopupTransition } from 'components/@extended/Transitions';
// assets
import { DeleteFilled } from '@ant-design/icons';
import { useDispatch } from 'store';
import { deleteTutorial } from 'store/reducers/tutorial';
// types
interface Props {
title: string;
open: boolean;
handleClose: (status: boolean) => void;
deleteId: number | string | undefined;
deleteId: string | undefined;
}
// ==============================|| Tutorial - DELETE ||============================== //
export default function AlertTutorialDelete({ title, open, handleClose, deleteId }: Props) {
// const dispatch = useDispatch();
const dispatch = useDispatch();
return (
<Dialog
......@@ -41,6 +43,14 @@ export default function AlertTutorialDelete({ title, open, handleClose, deleteId
<Typography variant="h4" align="center">
Are you sure you want to delete?
</Typography>
<Typography align="center">
By deleting
<Typography variant="subtitle1" component="span">
{' '}
"{title}"{' '}
</Typography>
tutorial, Its details & tasks will also be deleted.
</Typography>
</Stack>
<Stack direction="row" spacing={2} sx={{ width: 1 }}>
......@@ -48,7 +58,7 @@ export default function AlertTutorialDelete({ title, open, handleClose, deleteId
Cancel
</Button>
<Button fullWidth color="error" variant="contained" onClick={() => {
// dispatch(deleteNutrition(deleteId!))
dispatch(deleteTutorial(deleteId!))
handleClose(true)
}} autoFocus>
Delete
......
// third-party
import { createSlice } from '@reduxjs/toolkit';
// project imports
import { axiosServices } from 'utils/axios';
import { dispatch } from '../index';
// types
import { Curriculum, DefaultRootStateProps } from 'types/curriculum';
// ----------------------------------------------------------------------
const initialState: DefaultRootStateProps['curriculum'] = {
error: null,
success: null,
curriculums: [],
curriculum: null,
isLoading: false
};
const slice = createSlice({
name: 'curriculum',
initialState,
reducers: {
// TO INITIAL STATE
hasInitialState(state) {
state.error = null;
state.success = null;
state.isLoading = false;
},
// HAS ERROR
hasError(state, action) {
state.error = action.payload;
},
startLoading(state) {
state.isLoading = true;
},
finishLoading(state) {
state.isLoading = false;
},
// POST CURRICULUM
addCurriculumSuccess(state, action) {
state.curriculums.push(action.payload);
state.success = "Curriculum created successfully."
},
// GET CURRICULUM
fetchCurriculumSuccess(state, action) {
state.curriculum = action.payload;
state.success = null
},
// GET ALL CURRICULUM
fetchCurriculumsSuccess(state, action) {
state.curriculums = action.payload;
state.success = null
},
// UPDATE CURRICULUM
updateCurriculumSuccess(state, action) {
const updatedCurriculumIndex = state.curriculums.findIndex(curriculum => curriculum._id === action.payload._id);
if (updatedCurriculumIndex !== -1) {
state.curriculums[updatedCurriculumIndex] = action.payload;
}
state.success = "Curriculum updated successfully."
},
// DELETE CURRICULUM
deleteCurriculumSuccess(state, action) {
state.curriculums = state.curriculums.filter(curriculum => curriculum._id !== action.payload);
state.success = "Curriculum deleted successfully."
},
}
});
// Reducer
export default slice.reducer;
// ----------------------------------------------------------------------
/**
* TO INITIAL STATE
* @returns
*/
export function toInitialState() {
return async () => {
dispatch(slice.actions.hasInitialState())
}
}
/**
* POST CURRICULUM
* @param newCurriculum
* @returns
*/
export function addCurriculum(newCurriculum: Curriculum) {
return async () => {
dispatch(slice.actions.startLoading());
try {
const response = await axiosServices.post('/rest_node/curriculum', newCurriculum);
dispatch(slice.actions.addCurriculumSuccess(response.data));
} catch (error) {
dispatch(slice.actions.hasError(error));
} finally {
dispatch(slice.actions.finishLoading());
}
};
}
/**
* GET CURRICULUM
* @param id
* @returns
*/
export function fetchCurriculum(id: number) {
return async () => {
dispatch(slice.actions.startLoading());
try {
const response = await axiosServices.get(`/rest_node/curriculum/${id}`);
dispatch(slice.actions.fetchCurriculumSuccess(response.data));
} catch (error) {
dispatch(slice.actions.hasError(error));
} finally {
dispatch(slice.actions.finishLoading());
}
};
}
/**
* GET ALL CURRICULUMS
* @returns
*/
export function fetchCurriculums() {
return async () => {
dispatch(slice.actions.startLoading());
try {
const response = await axiosServices.get('/rest_node/curriculum');
dispatch(slice.actions.fetchCurriculumsSuccess(response.data));
} catch (error) {
dispatch(slice.actions.hasError(error));
} finally {
dispatch(slice.actions.finishLoading());
}
};
}
/**
* UPDATE CURRICULUM
* @param updatedCurriculum
* @returns
*/
export function updateCurriculum(updatedCurriculum: Curriculum) {
return async () => {
dispatch(slice.actions.startLoading());
try {
const response = await axiosServices.put(`/rest_node/curriculum/${updatedCurriculum._id}`, updatedCurriculum);
dispatch(slice.actions.updateCurriculumSuccess(response.data));
} catch (error) {
dispatch(slice.actions.hasError(error));
} finally {
dispatch(slice.actions.finishLoading());
}
};
}
/**
* DELETE CURRICULUM
* @param curriculumId
* @returns
*/
export function deleteCurriculum(curriculumId: string) {
return async () => {
dispatch(slice.actions.startLoading());
try {
await axiosServices.delete(`/rest_node/curriculum/${curriculumId}`);
dispatch(slice.actions.deleteCurriculumSuccess(curriculumId));
} catch (error) {
dispatch(slice.actions.hasError(error));
} finally {
dispatch(slice.actions.finishLoading());
}
};
}
......@@ -7,15 +7,19 @@ import storage from 'redux-persist/lib/storage';
import calendar from './calendar';
import cartReducer from './cart';
import chat from './chat';
import curriculum from './curriculum';
import ingredient from './ingredient';
import invoice from './invoice';
import kanban from './kanban';
import marksCalculator from './marksCalculator';
import menu from './menu';
import nutrition from './nutrition';
import productReducer from './product';
import snackbar from './snackbar';
import subscription from './subscription';
import tutorial from './tutorial';
import user from './user';
import userProgress from './userProgress';
// ==============================|| COMBINE REDUCERS ||============================== //
......@@ -37,7 +41,12 @@ const reducers = combineReducers({
invoice,
nutrition,
ingredient,
subscription
subscription,
marksCalculator,
tutorial,
curriculum,
user,
userProgress
});
export default reducers;
// third-party
import { createSlice } from '@reduxjs/toolkit';
// project imports
import { axiosServices } from 'utils/axios';
import { dispatch } from '../index';
// types
import { DefaultRootStateProps } from 'types/marksCalculator';
// ----------------------------------------------------------------------
const initialState: DefaultRootStateProps['marksCalculator'] = {
error: null,
success: null,
marksCalculator: null,
isLoading: false
};
const slice = createSlice({
name: 'marksCalculator',
initialState,
reducers: {
// TO INITIAL STATE
hasInitialState(state) {
state.error = null;
state.success = null;
state.isLoading = false;
},
// HAS ERROR
hasError(state, action) {
state.error = action.payload;
},
startLoading(state) {
state.isLoading = true;
},
finishLoading(state) {
state.isLoading = false;
},
// POST USER
marksCalculatorSuccess(state, action) {
state.marksCalculator = action.payload.result;
state.success = "Marks Calculated Successfully."
},
}
});
// Reducer
export default slice.reducer;
// ----------------------------------------------------------------------
/**
* TO INITIAL STATE
* @returns
*/
export function toInitialState() {
return async () => {
dispatch(slice.actions.hasInitialState())
}
}
/**
* POST Marks Calculator
* @param curriculumIndex
* @param tutorialIndex
* @param imageData
* @param targetClass
* @returns
*/
export function CalculateMarks(curriculumIndex: number, tutorialIndex: number, imageData: any, targetClass: string) {
return async () => {
dispatch(slice.actions.startLoading());
try {
// Construct the request body as needed (e.g., for formData)
const formData = new FormData();
formData.append('image', imageData);
formData.append('class', targetClass);
const response = await axiosServices.post(`/rest_node/marks-calculator/curriculum/${curriculumIndex}/tutorial/${tutorialIndex}`, formData);
dispatch(slice.actions.marksCalculatorSuccess(response.data));
} catch (error) {
dispatch(slice.actions.hasError(error));
} finally {
dispatch(slice.actions.finishLoading());
}
};
};
// third-party
import { createSlice } from '@reduxjs/toolkit';
// project imports
import { axiosServices } from 'utils/axios';
import { dispatch } from '../index';
// types
import { DefaultRootStateProps, Tutorial } from 'types/tutorial';
// ----------------------------------------------------------------------
const initialState: DefaultRootStateProps['tutorial'] = {
error: null,
success: null,
tutorials: [],
tutorial: null,
isLoading: false
};
const slice = createSlice({
name: 'tutorial',
initialState,
reducers: {
// TO INITIAL STATE
hasInitialState(state) {
state.error = null;
state.success = null;
state.isLoading = false;
},
// HAS ERROR
hasError(state, action) {
state.error = action.payload;
},
startLoading(state) {
state.isLoading = true;
},
finishLoading(state) {
state.isLoading = false;
},
// POST TUTORIAL
addTutorialSuccess(state, action) {
state.tutorials.push(action.payload);
state.success = "Tutorial created successfully."
},
// GET TUTORIAL
fetchTutorialSuccess(state, action) {
state.tutorial = action.payload;
state.success = null
},
// GET ALL TUTORIAL
fetchTutorialsSuccess(state, action) {
state.tutorials = action.payload;
state.success = null
},
// UPDATE TUTORIAL
updateTutorialSuccess(state, action) {
const updatedTutorialIndex = state.tutorials.findIndex(tutorial => tutorial._id === action.payload._id);
if (updatedTutorialIndex !== -1) {
state.tutorials[updatedTutorialIndex] = action.payload;
}
state.success = "Tutorial updated successfully."
},
// DELETE TUTORIAL
deleteTutorialSuccess(state, action) {
state.tutorials = state.tutorials.filter(tutorial => tutorial._id !== action.payload);
state.success = "Tutorial deleted successfully."
},
}
});
// Reducer
export default slice.reducer;
// ----------------------------------------------------------------------
/**
* TO INITIAL STATE
* @returns
*/
export function toInitialState() {
return async () => {
dispatch(slice.actions.hasInitialState())
}
}
/**
* POST TUTORIAL
* @param newTutorial
* @returns
*/
export function addTutorial(newTutorial: Tutorial) {
return async () => {
dispatch(slice.actions.startLoading());
try {
const response = await axiosServices.post('/rest_node/tutorial', newTutorial);
dispatch(slice.actions.addTutorialSuccess(response.data));
} catch (error) {
dispatch(slice.actions.hasError(error));
} finally {
dispatch(slice.actions.finishLoading());
}
};
}
/**
* GET TUTORIAL
* @param id
* @returns
*/
export function fetchTutorial(id: number) {
return async () => {
dispatch(slice.actions.startLoading());
try {
const response = await axiosServices.get(`/rest_node/tutorial/${id}`);
dispatch(slice.actions.fetchTutorialSuccess(response.data));
} catch (error) {
dispatch(slice.actions.hasError(error));
} finally {
dispatch(slice.actions.finishLoading());
}
};
}
/**
* GET ALL TUTORIALS
* @returns
*/
export function fetchTutorials() {
return async () => {
dispatch(slice.actions.startLoading());
try {
const response = await axiosServices.get('/rest_node/tutorial');
dispatch(slice.actions.fetchTutorialsSuccess(response.data));
} catch (error) {
dispatch(slice.actions.hasError(error));
} finally {
dispatch(slice.actions.finishLoading());
}
};
}
/**
* UPDATE TUTORIAL
* @param updatedTutorial
* @returns
*/
export function updateTutorial(updatedTutorial: Tutorial) {
return async () => {
dispatch(slice.actions.startLoading());
try {
const response = await axiosServices.put(`/rest_node/tutorial/${updatedTutorial._id}`, updatedTutorial);
dispatch(slice.actions.updateTutorialSuccess(response.data));
} catch (error) {
dispatch(slice.actions.hasError(error));
} finally {
dispatch(slice.actions.finishLoading());
}
};
}
/**
* DELETE TUTORIAL
* @param tutorialId
* @returns
*/
export function deleteTutorial(tutorialId: string) {
return async () => {
dispatch(slice.actions.startLoading());
try {
await axiosServices.delete(`/rest_node/tutorial/${tutorialId}`);
dispatch(slice.actions.deleteTutorialSuccess(tutorialId));
} catch (error) {
dispatch(slice.actions.hasError(error));
} finally {
dispatch(slice.actions.finishLoading());
}
};
}
......@@ -103,7 +103,7 @@ export function addUser(newUser: User) {
dispatch(slice.actions.startLoading());
try {
const response = await axiosServices.post('/user/sign-up', newUser);
const response = await axiosServices.post('/rest_node/user/sign-up', newUser);
dispatch(slice.actions.addUserSuccess(response.data));
} catch (error) {
dispatch(slice.actions.hasError(error));
......@@ -123,7 +123,7 @@ export function fetchUser(id: number) {
dispatch(slice.actions.startLoading());
try {
const response = await axiosServices.get(`/user/${id}`);
const response = await axiosServices.get(`/rest_node/user/${id}`);
dispatch(slice.actions.fetchUserSuccess(response.data));
} catch (error) {
dispatch(slice.actions.hasError(error));
......@@ -143,7 +143,7 @@ export function fetchUsers(userType: string) {
dispatch(slice.actions.startLoading());
try {
const response = await axiosServices.get(`/user/all/type/${userType}`);
const response = await axiosServices.get(`/rest_node/user/all/type/${userType}`);
dispatch(slice.actions.fetchUsersSuccess(response.data));
} catch (error) {
dispatch(slice.actions.hasError(error));
......@@ -162,7 +162,7 @@ export function fetchUsersByType() {
dispatch(slice.actions.startLoading());
try {
const response = await axiosServices.get('/user/all');
const response = await axiosServices.get('/rest_node/user/all');
dispatch(slice.actions.fetchUsersSuccess(response.data));
} catch (error) {
dispatch(slice.actions.hasError(error));
......@@ -182,7 +182,7 @@ export function updateUser(updatedUser: User) {
dispatch(slice.actions.startLoading());
try {
const response = await axiosServices.put(`/user/${updatedUser._id}`, updateUser);
const response = await axiosServices.put(`/rest_node/user/${updatedUser._id}`, updateUser);
dispatch(slice.actions.updateUserSuccess(response.data));
} catch (error) {
dispatch(slice.actions.hasError(error));
......@@ -202,7 +202,7 @@ export function deleteNutrition(userId: number) {
dispatch(slice.actions.startLoading());
try {
await axiosServices.delete(`/user/${userId}`);
await axiosServices.delete(`/rest_node/user/${userId}`);
dispatch(slice.actions.deleteUserSuccess(userId));
} catch (error) {
dispatch(slice.actions.hasError(error));
......
// third-party
import { createSlice } from '@reduxjs/toolkit';
// project imports
import { axiosServices } from 'utils/axios';
import { dispatch } from '../index';
// types
import { DefaultRootStateProps, curriculumTypeUserProgress } from 'types/userProgress';
// ----------------------------------------------------------------------
const initialState: DefaultRootStateProps['userProgress'] = {
error: null,
success: null,
userProgresses: [],
userProgress: null,
isLoading: false
};
const slice = createSlice({
name: 'userProgress',
initialState,
reducers: {
// TO INITIAL STATE
hasInitialState(state) {
state.error = null;
state.success = null;
state.isLoading = false;
},
// HAS ERROR
hasError(state, action) {
state.error = action.payload;
},
startLoading(state) {
state.isLoading = true;
},
finishLoading(state) {
state.isLoading = false;
},
// POST USER_PROGRESS
addUpdateUserProgressSuccess(state, action) {
// state.userProgresses.push(action.payload);
state.success = "User Progress created or updated successfully."
},
// GET USER_PROGRESS
fetchUserProgressSuccess(state, action) {
state.userProgress = action.payload.userProgress;
state.success = null
},
// UPDATE USER_PROGRESS
updateUserProgressSuccess(state, action) {
// const updatedUserProgressIndex = state.userProgresses.findIndex(userProgress => userProgress._id === action.payload._id);
// if (updatedUserProgressIndex !== -1) {
// state.userProgresses[updatedUserProgressIndex] = action.payload;
// }
state.success = "UserProgress updated successfully."
},
}
});
// Reducer
export default slice.reducer;
// ----------------------------------------------------------------------
/**
* TO INITIAL STATE
* @returns
*/
export function toInitialState() {
return async () => {
dispatch(slice.actions.hasInitialState())
}
}
/**
* POST USER_PROGRESS
* @param newUserProgress
* @returns
*/
export function addUserProgress(userId: string, curriculum: curriculumTypeUserProgress) {
return async () => {
dispatch(slice.actions.startLoading());
try {
const response = await axiosServices.post('/rest_node/user-progress/subscribe-curriculum', { userId, curriculum });
dispatch(slice.actions.addUpdateUserProgressSuccess(response.data));
} catch (error) {
dispatch(slice.actions.hasError(error));
} finally {
dispatch(slice.actions.finishLoading());
}
};
}
/**
* GET USER_PROGRESS
* @param userId
* @returns
*/
export function fetchUserProgress(userId: string) {
return async () => {
dispatch(slice.actions.startLoading());
try {
const response = await axiosServices.get(`/rest_node/user-progress/${userId}`);
dispatch(slice.actions.fetchUserProgressSuccess(response.data));
} catch (error) {
dispatch(slice.actions.hasError(error));
} finally {
dispatch(slice.actions.finishLoading());
}
};
}
/**
* UPDATE USER_PROGRESS
* @param updatedUserProgress
* @returns
*/
export function updateUserProgress(userId: string, curriculumCode: string, tutorialCode: string, taskItemTitle: string, taskItemMarkUser: number, taskItemSpentTime: number) {
return async () => {
dispatch(slice.actions.startLoading());
try {
const response = await axiosServices.put(`/rest_node/user-progress/update-task-item-progress`, { userId, curriculumCode, tutorialCode, taskItemTitle, taskItemMarkUser, taskItemSpentTime });
dispatch(slice.actions.updateUserProgressSuccess(response.data));
} catch (error) {
dispatch(slice.actions.hasError(error));
} finally {
dispatch(slice.actions.finishLoading());
}
};
}
import { tutorialType } from "./tutorial"
export interface curriculumType {
_id?: string
curriculumCode: string
curriculumLevel?: number
curriculumTitle?: string
curriculumDescription?: string
curriculumImage?: string
curriculumMark?: number
tutorials?: tutorialType[],
status?: number
createdBy?: string
createdAt?: Date
updatedBy?: string
updatedAt?: Date
subscribedUser?: string[]
}
export type Curriculum = {
_id?: string
curriculumCode: string
curriculumLevel: number
curriculumTitle: string
curriculumDescription: string
curriculumImage: string
curriculumMark?: number
tutorials: string[],
status?: number
createdBy: string
createdAt?: Date
updatedBy?: string
updatedAt?: Date
};
export type Curriculums = {
_id?: string
curriculumCode: string
curriculumLevel: number
curriculumTitle: string
curriculumDescription: string
curriculumImage: string
curriculumMark: number
tutorials: tutorialType[],
status: number
createdBy: string
createdAt: Date
updatedBy?: string
updatedAt?: Date
};
export interface CurriculumStateProps {
curriculums: Curriculums[];
curriculum: Curriculum | null;
error: object | string | null;
success: object | string | null;
isLoading: boolean
}
export interface DefaultRootStateProps {
curriculum: CurriculumStateProps;
}
\ No newline at end of file
// Marks Calculator Type
export type MarksCalculator = {
predicted_class_name: string,
confidence: string,
status: string
};
export interface MarksCalculatorProps {
marksCalculator: MarksCalculator | null;
error: object | string | null;
success: object | string | null;
isLoading: boolean
}
export interface DefaultRootStateProps {
marksCalculator: MarksCalculatorProps;
}
\ No newline at end of file
export interface taskItemType {
_id?: string
title: string,
description?: string,
howToDo?: string[],
referenceImage?: string,
referenceVideo?: string,
taskItemMark?: number
}
\ No newline at end of file
import { taskItemType } from "./taskItem"
export interface tutorialType {
_id?: string
tutorialCode: string
tutorialTitle?: string
tutorialDescription?: string
tutorialImage?: string
tutorialMark?: number
taskItems?: taskItemType[]
status?: number
createdBy?: string
createdAt?: Date
updatedBy?: string
updatedAt?: Date
}
export type Tutorial = {
_id?: string
tutorialCode?: string
tutorialTitle?: string
tutorialDescription?: string
tutorialImage?: string
tutorialMark?: number
taskItems: taskItemType[]
status?: number
createdBy: string
createdAt?: Date
updatedBy?: string
updatedAt?: Date
};
export type Tutorials = {
_id?: string
tutorialCode?: string
tutorialTitle?: string
tutorialDescription?: string
tutorialImage?: string
tutorialMark: number
taskItems: taskItemType[]
status: number
createdBy: string
createdAt: Date
updatedBy?: string
updatedAt?: Date
};
export interface TutorialStateProps {
tutorials: Tutorials[];
tutorial: Tutorial | null;
error: object | string | null;
success: object | string | null;
isLoading: boolean
}
export interface DefaultRootStateProps {
tutorial: TutorialStateProps;
}
\ No newline at end of file
export interface userProgressType {
_id: string
userId: string
curriculums?: curriculumTypeUserProgress[]
totalCurriculumsMarks: number
totalCurriculumSpentTime: number
status: number
createdBy?: string
createdAt: Date
updatedBy?: string
updatedAt?: Date
}
export interface curriculumTypeUserProgress {
curriculumCode: string
curriculumLevel?: number
curriculumTitle?: string
curriculumDescription?: string
curriculumImage?: string
curriculumMark?: number
curriculumMarkUser?: number
curriculumSpentTime?: number
tutorials?: tutorialTypeUserProgress[],
}
export interface tutorialTypeUserProgress {
tutorialCode: string
tutorialTitle?: string
tutorialDescription?: string
tutorialImage?: string
tutorialMarks?: number
tutorialMarkUser?: number
tutorialSpentTime?: number
taskItems?: taskItemTypeUserProgress[]
}
export interface taskItemTypeUserProgress {
title: string,
description?: string,
howToDo?: string[],
referenceImage?: string,
referenceVideo?: string,
taskItemMark?: number
taskItemMarkUser?: number
taskItemSpentTime?: number
}
export type UserProgress = {
_id: string
userId: string
curriculums?: curriculumTypeUserProgress[]
totalCurriculumsMarks: number
totalCurriculumSpentTime: number
status: number
createdBy?: string
createdAt: Date
updatedBy?: string
updatedAt?: Date
};
export type UserProgresses = {
_id?: string
userId: string
curriculums?: curriculumTypeUserProgress[]
totalCurriculumsMarks: number
totalCurriculumSpentTime: number
status: number
createdBy?: string
createdAt: Date
updatedBy?: string
updatedAt?: Date
};
export interface UserProgressStateProps {
userProgresses: UserProgresses[];
userProgress: UserProgress | null;
error: object | string | null;
success: object | string | null;
isLoading: boolean
}
export interface DefaultRootStateProps {
userProgress: UserProgressStateProps;
}
\ No newline at end of file
......@@ -161,9 +161,12 @@
"learning-curriculums-subscribed": "My Courses",
"learning-lead-board": "Lead Board",
"learning-feedback": "Feedback",
"ssl-translate": "SSL Translate",
"ssl-translate": "SSL Translation",
"emotion-detection": "Emotion Detection",
"audio-detection": "Audio Detection",
"video-detection": "Video Detection",
"learning-dashboard": "Dashboard"
}
\ No newline at end of file
"learning-dashboard": "Dashboard",
"learning-curriculums-subscribed-tutorial": "Tutorial",
"video-to-sign-language": "Sign Language Translate",
"video-translate": "Video Translator"
}
......@@ -11116,6 +11116,11 @@ react-transition-group@^4.4.0, react-transition-group@^4.4.5:
loose-envify "^1.4.0"
prop-types "^15.6.2"
react-unity-webgl@^9.4.3:
version "9.4.3"
resolved "https://registry.yarnpkg.com/react-unity-webgl/-/react-unity-webgl-9.4.3.tgz#c13cadb8fd447d817a128e6e05975c4f4c9b8f74"
integrity sha512-P/NhKil8EZRtT3q87DC0EgxdNd3c5wa34DMWr5128r2bZojgVIJJu+b20RtGaJpW4lxg6SB2PNN4+R2QujKlDw==
react-webcam@^7.1.1:
version "7.1.1"
resolved "https://registry.yarnpkg.com/react-webcam/-/react-webcam-7.1.1.tgz#e6290b192cde0d2a1039051a019a18e998d7fb39"
......
# Project ID: 2023-029
# Project Name: “Sign Language Translation with emotional base multi model,3D Avatar and Adaptive Learning.”
## Main Objective
......@@ -28,15 +29,40 @@ The main objective of this project is to develop a Sign Language Translation Sys
### Member: Ranaweera R M S H (IT20251000)
**Research Question:**
- How can sign language translation be achieved by identifying audio and text components in a video and translating them into sign language using 3D components?
- How can sign language translation be achieved by identifying the audio components in a video and the text, and translating them into sign language using 3D components?
**Objectives:**
- Pre-process video data to extract audio and text components.
- Develop techniques to translate audio and text into sign language.
- Integrate sign language translation with 3D components to create a visually appealing sign language video.
- Evaluate the accuracy of sign language translation and the overall effectiveness of the demonstration.
- Pre-process video data to extract audio components.
- Develop techniques to translate audio into sign language.
- Develop techniques to translate text into sign language.
- Integrate sign language translation with 3D components to create visually appealing sign language videos.
- Evaluate the accuracy of sign language translation.
- Evaluate the overall effectiveness of the sign language translation demonstration.
- Apply computer graphics techniques to enhance the realism and visual quality of the sign language animations.
## Novelty and Contributions
- **Novel Approach:**
> The research question proposes a novel approach to sign language translation by utilizing audio components from a video and text input. This approach combines audio analysis and text translation with 3D components to create visually appealing sign language videos.
- **Audio Component Extraction:**
> The research aims to develop techniques to pre-process video data and extract audio components. This extraction process is crucial for identifying and analyzing the audio elements necessary for sign language translation.
- **Audio-to-Sign Language Translation:**
> The research contributes to the development of techniques that translate audio components into sign language. By analyzing the audio data, the system can generate accurate sign language gestures and expressions corresponding to the spoken words or sounds.
- **Text-to-Sign Language Translation:**
> Another significant contribution is the development of techniques to translate text input into sign language. This enables users to input text directly, which the system converts into sign language gestures and expressions, providing a means of communication for individuals who are deaf or hard of hearing.
- **Integration of 3D Components:**
> The research focuses on integrating sign language translation with 3D components to enhance the visual quality and realism of the sign language animations. This integration adds depth and realism to the sign language gestures and creates visually appealing videos that effectively convey the intended message.
- **Evaluation of Translation Accuracy:**
> The research evaluates the accuracy of the sign language translation by comparing the generated sign language gestures with established sign language conventions. This evaluation ensures that the translations are linguistically and culturally appropriate, enhancing the overall effectiveness of the system.
- **Computer Graphics Enhancements:**
> The research contributes to the application of computer graphics techniques to enhance the realism and visual quality of the sign language animations. By leveraging graphics capabilities, the system can create visually engaging and expressive sign language videos, improving the user experience.
### Member: A V R Dilshan (IT20005276)
**Research Question:**
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment