Merge pull request #442 from JustFlavio/refactor/course-creation-tags

feat: replace textareas with FormTagInput for tags and learnings
This commit is contained in:
Badr B. 2025-02-26 18:57:02 +01:00 committed by GitHub
commit 6878fec2e8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 1482 additions and 49 deletions

View file

@ -30,7 +30,7 @@ const CourseClient = (props: any) => {
function getLearningTags() {
// create array of learnings from a string object (comma separated)
let learnings = course?.learnings ? course?.learnings.split(',') : []
let learnings = course?.learnings ? course?.learnings.split('|') : []
setLearnings(learnings)
}

View file

@ -10,6 +10,7 @@ import * as Form from '@radix-ui/react-form';
import React, { useEffect, useState } from 'react';
import ThumbnailUpdate from './ThumbnailUpdate';
import { useCourse, useCourseDispatch } from '@components/Contexts/CourseContext';
import FormTagInput from '@components/Objects/StyledElements/Form/TagInput';
type EditCourseStructureProps = {
orgslug: string
@ -138,24 +139,22 @@ function EditCourseGeneral(props: EditCourseStructureProps) {
<FormField name="learnings">
<FormLabelAndMessage label="Learnings" message={formik.errors.learnings} />
<Form.Control asChild>
<Textarea
style={{ backgroundColor: 'white' }}
onChange={formik.handleChange}
value={formik.values.learnings}
required
/>
<FormTagInput
placeholder="Enter to add..."
onChange={(value) => formik.setFieldValue('learnings', value)}
value={formik.values.learnings}
/>
</Form.Control>
</FormField>
<FormField name="tags">
<FormLabelAndMessage label="Tags" message={formik.errors.tags} />
<Form.Control asChild>
<Textarea
style={{ backgroundColor: 'white' }}
onChange={formik.handleChange}
value={formik.values.tags}
required
/>
<FormTagInput
placeholder="Enter to add..."
onChange={(value) => formik.setFieldValue('tags', value)}
value={formik.values.tags}
/>
</Form.Control>
</FormField>

View file

@ -19,6 +19,7 @@ import { useFormik } from 'formik'
import * as Yup from 'yup'
import { UploadCloud, Image as ImageIcon } from 'lucide-react'
import UnsplashImagePicker from "@components/Dashboard/Pages/Course/EditCourseGeneral/UnsplashImagePicker"
import FormTagInput from "@components/Objects/StyledElements/Form/TagInput"
const validationSchema = Yup.object().shape({
name: Yup.string()
@ -58,6 +59,7 @@ function CreateCourseModal({ closeModal, orgslug }: any) {
{
name: values.name,
description: values.description,
learnings: values.learnings,
tags: values.tags,
visibility: values.visibility
},
@ -200,19 +202,31 @@ function CreateCourseModal({ closeModal, orgslug }: any) {
</div>
</FormField>
<FormField name="tags">
<FormLabelAndMessage
label="Course Tags"
message={formik.errors.tags}
/>
<Form.Control asChild>
<Textarea
onChange={formik.handleChange}
value={formik.values.tags}
placeholder="Enter tags separated by commas"
/>
</Form.Control>
</FormField>
<FormField name="learnings">
<FormLabelAndMessage
label="Course Learnings (What will you teach?)"
message={formik.errors.learnings}
/>
<FormTagInput
placeholder="Enter to add..."
value={formik.values.learnings}
onChange={(value) => formik.setFieldValue('learnings', value)}
error={formik.errors.learnings}
/>
</FormField>
<FormField name="tags">
<FormLabelAndMessage
label="Course Tags"
message={formik.errors.tags}
/>
<FormTagInput
placeholder="Enter to add..."
value={formik.values.tags}
onChange={(value) => formik.setFieldValue('tags', value)}
error={formik.errors.tags}
/>
</FormField>
<FormField name="visibility">
<FormLabelAndMessage

View file

@ -0,0 +1,83 @@
import React, { useState, Dispatch, SetStateAction, useEffect } from 'react'
import { Tag, TagInput } from 'emblor'
interface FormTagInputProps {
value: string
onChange: (value: string) => void
separator?: string
error?: string
placeholder?: string
}
const FormTagInput = ({
value,
onChange,
separator = '|',
error,
placeholder,
}: FormTagInputProps) => {
const [tags, setTags] = useState<Tag[]>(() =>
value && typeof value === 'string'
? value.split(separator).filter(text => text.trim()).map((text, i) => ({
id: i.toString(),
text: text.trim(),
}))
: []
);
useEffect(() => {
if (value && typeof value === 'string') {
const newTags = value.split(separator)
.filter(text => text.trim())
.map((text, i) => ({
id: i.toString(),
text: text.trim(),
}));
setTags(newTags);
} else {
setTags([]);
}
}, [value, separator]);
const [activeTagIndex, setActiveTagIndex] = useState<number | null>(null)
const handleTagsChange: Dispatch<SetStateAction<Tag[]>> = (
newTagsOrUpdater
) => {
const newTags =
typeof newTagsOrUpdater === 'function'
? newTagsOrUpdater(tags)
: newTagsOrUpdater
setTags(newTags)
onChange(newTags.map((tag) => tag.text).join(separator))
}
return (
<div>
<div className="space-y-2">
<TagInput
tags={tags}
setTags={handleTagsChange}
placeholder={placeholder}
styleClasses={{
inlineTagsContainer:
'border-input rounded-lg bg-background shadow-xs transition-shadow focus-within:border-ring/40 focus-within:outline-hidden focus-within:ring-[3px] ring-ring/8 dark:ring-ring/12 p-1 gap-1',
input:
'w-full min-w-[80px] focus-visible:outline-hidden shadow-none px-2 h-7',
tag: {
body: 'h-7 relative bg-background border border-input hover:bg-background rounded-md font-medium text-xs ps-2 pe-7',
closeButton:
'absolute -inset-y-px -end-px p-0 rounded-e-lg flex size-7 transition-colors outline-hidden focus-visible:ring-2 focus-visible:ring-ring/30 dark:focus-visible:ring-ring/40 text-muted-foreground/80 hover:text-foreground',
},
}}
activeTagIndex={activeTagIndex}
setActiveTagIndex={setActiveTagIndex}
/>
{error && <p className="text-sm font-medium text-destructive">{error}</p>}
</div>
</div>
)
}
export default FormTagInput

View file

@ -50,6 +50,7 @@
"currency-codes": "^2.2.0",
"dayjs": "^1.11.13",
"dompurify": "^3.2.4",
"emblor": "^1.4.7",
"formik": "^2.4.6",
"framer-motion": "^10.18.0",
"get-youtube-id": "^1.0.1",

1354
apps/web/pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff

View file

@ -102,7 +102,7 @@ export async function createNewCourse(
formData.append('name', course_body.name)
formData.append('description', course_body.description)
formData.append('public', course_body.visibility)
formData.append('learnings', course_body.tags)
formData.append('learnings', course_body.learnings)
formData.append('tags', course_body.tags)
formData.append('about', course_body.description)