mirror of
https://github.com/rzmk/learnhouse.git
synced 2025-12-19 04:19:25 +00:00
feat: implement FormTagInput for learnings and tags in course creation & edit forms.
This commit is contained in:
parent
000f1031e7
commit
cdd05f6476
5 changed files with 121 additions and 40 deletions
|
|
@ -30,7 +30,7 @@ const CourseClient = (props: any) => {
|
||||||
|
|
||||||
function getLearningTags() {
|
function getLearningTags() {
|
||||||
// create array of learnings from a string object (comma separated)
|
// 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)
|
setLearnings(learnings)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import * as Form from '@radix-ui/react-form';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import ThumbnailUpdate from './ThumbnailUpdate';
|
import ThumbnailUpdate from './ThumbnailUpdate';
|
||||||
import { useCourse, useCourseDispatch } from '@components/Contexts/CourseContext';
|
import { useCourse, useCourseDispatch } from '@components/Contexts/CourseContext';
|
||||||
|
import FormTagInput from '@components/Objects/StyledElements/Form/TagInput';
|
||||||
|
|
||||||
type EditCourseStructureProps = {
|
type EditCourseStructureProps = {
|
||||||
orgslug: string
|
orgslug: string
|
||||||
|
|
@ -138,11 +139,10 @@ function EditCourseGeneral(props: EditCourseStructureProps) {
|
||||||
<FormField name="learnings">
|
<FormField name="learnings">
|
||||||
<FormLabelAndMessage label="Learnings" message={formik.errors.learnings} />
|
<FormLabelAndMessage label="Learnings" message={formik.errors.learnings} />
|
||||||
<Form.Control asChild>
|
<Form.Control asChild>
|
||||||
<Textarea
|
<FormTagInput
|
||||||
style={{ backgroundColor: 'white' }}
|
placeholder="Enter to add..."
|
||||||
onChange={formik.handleChange}
|
onChange={(value) => formik.setFieldValue('learnings', value)}
|
||||||
value={formik.values.learnings}
|
value={formik.values.learnings}
|
||||||
required
|
|
||||||
/>
|
/>
|
||||||
</Form.Control>
|
</Form.Control>
|
||||||
</FormField>
|
</FormField>
|
||||||
|
|
@ -150,11 +150,10 @@ function EditCourseGeneral(props: EditCourseStructureProps) {
|
||||||
<FormField name="tags">
|
<FormField name="tags">
|
||||||
<FormLabelAndMessage label="Tags" message={formik.errors.tags} />
|
<FormLabelAndMessage label="Tags" message={formik.errors.tags} />
|
||||||
<Form.Control asChild>
|
<Form.Control asChild>
|
||||||
<Textarea
|
<FormTagInput
|
||||||
style={{ backgroundColor: 'white' }}
|
placeholder="Enter to add..."
|
||||||
onChange={formik.handleChange}
|
onChange={(value) => formik.setFieldValue('tags', value)}
|
||||||
value={formik.values.tags}
|
value={formik.values.tags}
|
||||||
required
|
|
||||||
/>
|
/>
|
||||||
</Form.Control>
|
</Form.Control>
|
||||||
</FormField>
|
</FormField>
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import { useFormik } from 'formik'
|
||||||
import * as Yup from 'yup'
|
import * as Yup from 'yup'
|
||||||
import { UploadCloud, Image as ImageIcon } from 'lucide-react'
|
import { UploadCloud, Image as ImageIcon } from 'lucide-react'
|
||||||
import UnsplashImagePicker from "@components/Dashboard/Pages/Course/EditCourseGeneral/UnsplashImagePicker"
|
import UnsplashImagePicker from "@components/Dashboard/Pages/Course/EditCourseGeneral/UnsplashImagePicker"
|
||||||
|
import FormTagInput from "@components/Objects/StyledElements/Form/TagInput"
|
||||||
|
|
||||||
const validationSchema = Yup.object().shape({
|
const validationSchema = Yup.object().shape({
|
||||||
name: Yup.string()
|
name: Yup.string()
|
||||||
|
|
@ -58,6 +59,7 @@ function CreateCourseModal({ closeModal, orgslug }: any) {
|
||||||
{
|
{
|
||||||
name: values.name,
|
name: values.name,
|
||||||
description: values.description,
|
description: values.description,
|
||||||
|
learnings: values.learnings,
|
||||||
tags: values.tags,
|
tags: values.tags,
|
||||||
visibility: values.visibility
|
visibility: values.visibility
|
||||||
},
|
},
|
||||||
|
|
@ -200,18 +202,30 @@ function CreateCourseModal({ closeModal, orgslug }: any) {
|
||||||
</div>
|
</div>
|
||||||
</FormField>
|
</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">
|
<FormField name="tags">
|
||||||
<FormLabelAndMessage
|
<FormLabelAndMessage
|
||||||
label="Course Tags"
|
label="Course Tags"
|
||||||
message={formik.errors.tags}
|
message={formik.errors.tags}
|
||||||
/>
|
/>
|
||||||
<Form.Control asChild>
|
<FormTagInput
|
||||||
<Textarea
|
placeholder="Enter to add..."
|
||||||
onChange={formik.handleChange}
|
|
||||||
value={formik.values.tags}
|
value={formik.values.tags}
|
||||||
placeholder="Enter tags separated by commas"
|
onChange={(value) => formik.setFieldValue('tags', value)}
|
||||||
|
error={formik.errors.tags}
|
||||||
/>
|
/>
|
||||||
</Form.Control>
|
|
||||||
</FormField>
|
</FormField>
|
||||||
|
|
||||||
<FormField name="visibility">
|
<FormField name="visibility">
|
||||||
|
|
|
||||||
68
apps/web/components/Objects/StyledElements/Form/TagInput.tsx
Normal file
68
apps/web/components/Objects/StyledElements/Form/TagInput.tsx
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
import React, { useState, Dispatch, SetStateAction } 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 initialTags = value
|
||||||
|
? value.split(separator).map((text, i) => ({
|
||||||
|
id: i.toString(),
|
||||||
|
text: text.trim(),
|
||||||
|
}))
|
||||||
|
: []
|
||||||
|
|
||||||
|
const [tags, setTags] = useState<Tag[]>(initialTags)
|
||||||
|
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
|
||||||
|
|
@ -102,7 +102,7 @@ export async function createNewCourse(
|
||||||
formData.append('name', course_body.name)
|
formData.append('name', course_body.name)
|
||||||
formData.append('description', course_body.description)
|
formData.append('description', course_body.description)
|
||||||
formData.append('public', course_body.visibility)
|
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('tags', course_body.tags)
|
||||||
formData.append('about', course_body.description)
|
formData.append('about', course_body.description)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue