mirror of
https://github.com/Sonny93/my-links.git
synced 2025-12-08 22:53:25 +00:00
feat: add optionnal category description
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE `category` ADD COLUMN `description` VARCHAR(255) NULL;
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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) => (
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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');
|
||||
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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'
|
||||
|
||||
Reference in New Issue
Block a user