feat: add optionnal category description

This commit is contained in:
Sonny
2024-04-10 18:26:23 +02:00
parent 883b36c93e
commit 42a5dabec1
12 changed files with 73 additions and 34 deletions

View File

@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE `category` ADD COLUMN `description` VARCHAR(255) NULL;

View File

@@ -29,9 +29,10 @@ model User {
}
model Category {
id Int @id @default(autoincrement())
name String @db.VarChar(255)
links Link[]
id Int @id @default(autoincrement())
name String @db.VarChar(255)
description String? @db.VarChar(255)
links Link[]
author User @relation(fields: [authorId], references: [id])
authorId Int
@@ -45,10 +46,10 @@ model Category {
}
model Link {
id Int @id @default(autoincrement())
name String @db.VarChar(255)
description String? @db.VarChar(255)
url String @db.Text
id Int @id @default(autoincrement())
name String @db.VarChar(255)
description String @db.VarChar(255)
url String @db.Text
category Category @relation(fields: [categoryId], references: [id])
categoryId Int

View File

@@ -18,6 +18,7 @@
"categories": "Categories",
"category": "Category",
"name": "Category name",
"description": "Category description",
"create": "Create a category",
"edit": "Edit a category",
"remove": "Delete a category",
@@ -46,4 +47,4 @@
"footer": {
"made_by": "Made with ❤\uFE0F by"
}
}
}

View File

@@ -18,6 +18,7 @@
"categories": "Catégories",
"category": "Catégorie",
"name": "Nom de la catégorie",
"description": "Description de la catégorie",
"create": "Créer une catégorie",
"edit": "Modifier une catégorie",
"remove": "Supprimer une catégorie",
@@ -46,4 +47,4 @@
"footer": {
"made_by": "Fait avec ❤\uFE0F par"
}
}
}

View File

@@ -66,6 +66,11 @@ export default function Links({
/>
</span>
</h2>
{activeCategory.description && (
<p className={styles['category-description']}>
{activeCategory.description}
</p>
)}
{links.length !== 0 ? (
<ul className={clsx(styles['links'], 'reset')}>
{links.map((link, index) => (

View File

@@ -29,24 +29,15 @@
flex: 1;
flex-direction: column;
& h2 {
color: $blue;
& .category-description {
font-size: 0.85em;
margin-bottom: 0.5em;
font-weight: 500;
& svg {
display: flex;
}
& .links-count {
color: $grey;
font-weight: 300;
font-size: 0.8em;
}
}
}
.category-header {
color: $blue;
font-weight: 500;
display: flex;
gap: 0.4em;
align-items: center;
@@ -65,6 +56,16 @@
gap: 0.5em;
align-items: center;
}
& svg {
display: flex;
}
& .links-count {
color: $grey;
font-weight: 300;
font-size: 0.8em;
}
}
.links {

View File

@@ -5,6 +5,7 @@ const CategoryBodySchema = object({
.trim()
.required('Category name is required')
.max(128, 'Category name is too long'),
description: string().trim().max(255, 'Category description is too long'),
nextId: number().required().nullable(),
}).typeError('Missing request Body');

View File

@@ -16,7 +16,9 @@ export default apiHandler({
async function editCategory({ req, res, user }) {
const { cid } = await CategoryQuerySchema.validate(req.query);
const { name, nextId } = await CategoryBodySchema.validate(req.body);
const { name, description, nextId } = await CategoryBodySchema.validate(
req.body,
);
const userId = user.id as User['id'];
const category = await getUserCategory(user, cid);
@@ -104,6 +106,7 @@ async function editCategory({ req, res, user }) {
},
data: {
name,
description,
nextId: category.nextId,
},
});

View File

@@ -18,7 +18,7 @@ async function getCategories({ res, user }) {
}
async function createCategory({ req, res, user }) {
const { name } = await CategoryBodySchema.validate(req.body);
const { name, description } = await CategoryBodySchema.validate(req.body);
const category = await getUserCategoryByName(user, name);
if (category) {
@@ -36,7 +36,7 @@ async function createCategory({ req, res, user }) {
});
const categoryCreated = await prisma.category.create({
data: { name, authorId: user.id },
data: { name, description, authorId: user.id },
});
if (lastCategory) {

View File

@@ -23,6 +23,7 @@ export default function PageCreateCategory({
const info = useRouter().query?.info as string;
const [name, setName] = useState<string>('');
const [description, setDescription] = useState<string>('');
const [error, setError] = useState<string | null>(null);
const [submitted, setSubmitted] = useState<boolean>(false);
@@ -40,7 +41,7 @@ export default function PageCreateCategory({
makeRequest({
url: PATHS.API.CATEGORY,
method: 'POST',
body: { name, nextId: null },
body: { name, description, nextId: null },
})
.then((data) =>
router.push(`${PATHS.HOME}?categoryId=${data?.categoryId}`),
@@ -62,11 +63,20 @@ export default function PageCreateCategory({
<TextBox
name='name'
label={t('common:category.name')}
onChangeCallback={(value) => setName(value)}
onChangeCallback={setName}
value={name}
fieldClass={styles['input-field']}
placeholder={t('common:category.name')}
innerRef={autoFocusRef}
required
/>
<TextBox
name='description'
label={t('common:category.description')}
onChangeCallback={setDescription}
value={description}
fieldClass={styles['input-field']}
placeholder={t('common:category.description')}
/>
</FormLayout>
</PageTransition>

View File

@@ -23,14 +23,17 @@ export default function PageEditCategory({
const autoFocusRef = useAutoFocus();
const [name, setName] = useState<string>(category.name);
const [description, setDescription] = useState<string>(category.description);
const [error, setError] = useState<string | null>(null);
const [submitted, setSubmitted] = useState<boolean>(false);
const canSubmit = useMemo<boolean>(
() => name !== category.name && name !== '' && !submitted,
[category.name, name, submitted],
);
const canSubmit = useMemo<boolean>(() => {
const isFormEdited =
name !== category.name || description !== category.description;
const isFormValid = name !== '';
return isFormEdited && isFormValid && !submitted;
}, [category.description, category.name, description, name, submitted]);
const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
@@ -40,7 +43,7 @@ export default function PageEditCategory({
makeRequest({
url: `${PATHS.API.CATEGORY}/${category.id}`,
method: 'PUT',
body: { name, nextId: category.nextId },
body: { name, description, nextId: category.nextId },
})
.then((data) =>
router.push(`${PATHS.HOME}?categoryId=${data?.categoryId}`),
@@ -60,11 +63,20 @@ export default function PageEditCategory({
<TextBox
name='name'
label={t('common:category.name')}
onChangeCallback={(value) => setName(value)}
onChangeCallback={setName}
value={name}
fieldClass={styles['input-field']}
placeholder={`${t('common:category.name')} : ${category.name}`}
innerRef={autoFocusRef}
required
/>
<TextBox
name='description'
label={t('common:category.description')}
onChangeCallback={setDescription}
value={description}
fieldClass={styles['input-field']}
placeholder={t('common:category.description')}
/>
</FormLayout>
</PageTransition>

View File

@@ -104,7 +104,9 @@ export default function PageEditLink({
onChangeCallback={setDescription}
value={description}
fieldClass={styles['input-field']}
placeholder={`${t('common:link.description')} : ${link.description}`}
placeholder={`${t('common:link.description')}${
` : ${link.description}` ?? ''
}`}
/>
<Selector
name='category'