mirror of
https://github.com/Sonny93/my-links.git
synced 2025-12-08 22:53:25 +00:00
feat: create edit collection page
This commit is contained in:
@@ -1,13 +1,16 @@
|
|||||||
import PATHS from '#constants/paths';
|
import PATHS from '#constants/paths';
|
||||||
import Collection from '#models/collection';
|
import Collection from '#models/collection';
|
||||||
import User from '#models/user';
|
import User from '#models/user';
|
||||||
import { collectionValidator } from '#validators/collection';
|
import {
|
||||||
|
createCollectionValidator,
|
||||||
|
updateCollectionValidator,
|
||||||
|
} from '#validators/collection';
|
||||||
import type { HttpContext } from '@adonisjs/core/http';
|
import type { HttpContext } from '@adonisjs/core/http';
|
||||||
|
|
||||||
export default class CollectionsController {
|
export default class CollectionsController {
|
||||||
// Dashboard
|
// Dashboard
|
||||||
async index({ auth, inertia, request, response }: HttpContext) {
|
async index({ auth, inertia, request, response }: HttpContext) {
|
||||||
const collections = await this.getCollectionByAuthorId(auth.user!.id);
|
const collections = await this.getCollectionsByAuthorId(auth.user!.id);
|
||||||
if (collections.length === 0) {
|
if (collections.length === 0) {
|
||||||
return response.redirect('/collections/create');
|
return response.redirect('/collections/create');
|
||||||
}
|
}
|
||||||
@@ -29,7 +32,7 @@ export default class CollectionsController {
|
|||||||
|
|
||||||
// Create collection form
|
// Create collection form
|
||||||
async showCreatePage({ inertia, auth }: HttpContext) {
|
async showCreatePage({ inertia, auth }: HttpContext) {
|
||||||
const collections = await this.getCollectionByAuthorId(auth.user!.id);
|
const collections = await this.getCollectionsByAuthorId(auth.user!.id);
|
||||||
return inertia.render('collections/create', {
|
return inertia.render('collections/create', {
|
||||||
disableHomeLink: collections.length === 0,
|
disableHomeLink: collections.length === 0,
|
||||||
});
|
});
|
||||||
@@ -37,7 +40,7 @@ export default class CollectionsController {
|
|||||||
|
|
||||||
// Method called when creating a collection
|
// Method called when creating a collection
|
||||||
async store({ request, response, auth }: HttpContext) {
|
async store({ request, response, auth }: HttpContext) {
|
||||||
const payload = await request.validateUsing(collectionValidator);
|
const payload = await request.validateUsing(createCollectionValidator);
|
||||||
const collection = await Collection.create({
|
const collection = await Collection.create({
|
||||||
...payload,
|
...payload,
|
||||||
authorId: auth.user?.id!,
|
authorId: auth.user?.id!,
|
||||||
@@ -45,11 +48,54 @@ export default class CollectionsController {
|
|||||||
return this.redirectToCollectionId(response, collection.id);
|
return this.redirectToCollectionId(response, collection.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getCollectionById(id: Collection['id']) {
|
async showEditPage({ auth, request, inertia, response }: HttpContext) {
|
||||||
return await Collection.find(id);
|
const collectionId = request.qs()?.collectionId;
|
||||||
|
if (!collectionId) {
|
||||||
|
return response.redirect('/dashboard');
|
||||||
|
}
|
||||||
|
|
||||||
|
const collection = await this.getCollectionById(
|
||||||
|
collectionId,
|
||||||
|
auth.user!.id
|
||||||
|
);
|
||||||
|
return inertia.render('collections/edit', {
|
||||||
|
collection,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async getCollectionByAuthorId(authorId: User['id']) {
|
/**
|
||||||
|
* Get collection by id.
|
||||||
|
*
|
||||||
|
* /!\ Only return private collection (create by the current user)
|
||||||
|
*/
|
||||||
|
async getCollectionById(id: Collection['id'], userId: User['id']) {
|
||||||
|
return await Collection.query()
|
||||||
|
.where('id', id)
|
||||||
|
.andWhere('author_id', userId)
|
||||||
|
.firstOrFail();
|
||||||
|
}
|
||||||
|
|
||||||
|
async update({ request, auth, response }: HttpContext) {
|
||||||
|
const { params, ...payload } = await request.validateUsing(
|
||||||
|
updateCollectionValidator
|
||||||
|
);
|
||||||
|
|
||||||
|
// Cant use validator (vinejs) custom rule 'cause its too generic,
|
||||||
|
// because we have to find a collection by identifier and
|
||||||
|
// check whether the current user is the author.
|
||||||
|
// https://vinejs.dev/docs/extend/custom_rules
|
||||||
|
await this.getCollectionById(params.id, auth.user!.id);
|
||||||
|
|
||||||
|
await Collection.updateOrCreate(
|
||||||
|
{
|
||||||
|
id: params.id,
|
||||||
|
},
|
||||||
|
payload
|
||||||
|
);
|
||||||
|
return this.redirectToCollectionId(response, params.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCollectionsByAuthorId(authorId: User['id']) {
|
||||||
return await Collection.query()
|
return await Collection.query()
|
||||||
.where('author_id', authorId)
|
.where('author_id', authorId)
|
||||||
.preload('links');
|
.preload('links');
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export default class LinksController {
|
|||||||
|
|
||||||
async showCreatePage({ auth, inertia }: HttpContext) {
|
async showCreatePage({ auth, inertia }: HttpContext) {
|
||||||
const collections =
|
const collections =
|
||||||
await this.collectionsController.getCollectionByAuthorId(auth.user!.id);
|
await this.collectionsController.getCollectionsByAuthorId(auth.user!.id);
|
||||||
return inertia.render('links/create', { collections });
|
return inertia.render('links/create', { collections });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -18,7 +18,10 @@ export default class LinksController {
|
|||||||
const { collectionId, ...payload } =
|
const { collectionId, ...payload } =
|
||||||
await request.validateUsing(linkValidator);
|
await request.validateUsing(linkValidator);
|
||||||
|
|
||||||
await this.collectionsController.getCollectionById(collectionId);
|
await this.collectionsController.getCollectionById(
|
||||||
|
collectionId,
|
||||||
|
auth.user!.id
|
||||||
|
);
|
||||||
await Link.create({
|
await Link.create({
|
||||||
...payload,
|
...payload,
|
||||||
collectionId,
|
collectionId,
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
|
import PATHS from '#constants/paths';
|
||||||
|
import { ExceptionHandler, HttpContext } from '@adonisjs/core/http';
|
||||||
import app from '@adonisjs/core/services/app';
|
import app from '@adonisjs/core/services/app';
|
||||||
import { HttpContext, ExceptionHandler } from '@adonisjs/core/http';
|
import type {
|
||||||
import type { StatusPageRange, StatusPageRenderer } from '@adonisjs/core/types/http';
|
StatusPageRange,
|
||||||
|
StatusPageRenderer,
|
||||||
|
} from '@adonisjs/core/types/http';
|
||||||
|
import { errors } from '@adonisjs/lucid';
|
||||||
|
|
||||||
export default class HttpExceptionHandler extends ExceptionHandler {
|
export default class HttpExceptionHandler extends ExceptionHandler {
|
||||||
/**
|
/**
|
||||||
@@ -21,8 +26,10 @@ export default class HttpExceptionHandler extends ExceptionHandler {
|
|||||||
* to return the HTML contents to send as a response.
|
* to return the HTML contents to send as a response.
|
||||||
*/
|
*/
|
||||||
protected statusPages: Record<StatusPageRange, StatusPageRenderer> = {
|
protected statusPages: Record<StatusPageRange, StatusPageRenderer> = {
|
||||||
'404': (error, { inertia }) => inertia.render('errors/not_found', { error }),
|
'404': (error, { inertia }) =>
|
||||||
'500..599': (error, { inertia }) => inertia.render('errors/server_error', { error }),
|
inertia.render('errors/not_found', { error }),
|
||||||
|
'500..599': (error, { inertia }) =>
|
||||||
|
inertia.render('errors/server_error', { error }),
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -30,6 +37,9 @@ export default class HttpExceptionHandler extends ExceptionHandler {
|
|||||||
* response to the client
|
* response to the client
|
||||||
*/
|
*/
|
||||||
async handle(error: unknown, ctx: HttpContext) {
|
async handle(error: unknown, ctx: HttpContext) {
|
||||||
|
if (error instanceof errors.E_ROW_NOT_FOUND) {
|
||||||
|
return ctx.response.redirect(PATHS.DASHBOARD);
|
||||||
|
}
|
||||||
return super.handle(error, ctx);
|
return super.handle(error, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,29 @@
|
|||||||
import vine, { SimpleMessagesProvider } from '@vinejs/vine';
|
import vine, { SimpleMessagesProvider } from '@vinejs/vine';
|
||||||
import { Visibility } from '../enums/visibility.js';
|
import { Visibility } from '../enums/visibility.js';
|
||||||
|
|
||||||
export const collectionValidator = vine.compile(
|
export const createCollectionValidator = vine.compile(
|
||||||
vine.object({
|
vine.object({
|
||||||
name: vine.string().trim().minLength(1).maxLength(254),
|
name: vine.string().trim().minLength(1).maxLength(254),
|
||||||
description: vine.string().trim().maxLength(300).optional(),
|
description: vine.string().trim().maxLength(254).nullable(),
|
||||||
visibility: vine.enum(Visibility),
|
visibility: vine.enum(Visibility),
|
||||||
nextId: vine.string().optional(),
|
nextId: vine.string().optional(),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
collectionValidator.messagesProvider = new SimpleMessagesProvider({
|
export const updateCollectionValidator = vine.compile(
|
||||||
|
vine.object({
|
||||||
|
name: vine.string().trim().minLength(1).maxLength(254),
|
||||||
|
description: vine.string().trim().maxLength(254).nullable(),
|
||||||
|
visibility: vine.enum(Visibility),
|
||||||
|
nextId: vine.string().optional(),
|
||||||
|
|
||||||
|
params: vine.object({
|
||||||
|
id: vine.string().trim(),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
createCollectionValidator.messagesProvider = new SimpleMessagesProvider({
|
||||||
name: 'Collection name is required',
|
name: 'Collection name is required',
|
||||||
'visibility.required': 'Collection visibiliy is required',
|
'visibility.required': 'Collection visibiliy is required',
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { Link } from '@inertiajs/react';
|
import { Link } from '@inertiajs/react';
|
||||||
|
|
||||||
const DropdownItemBase = styled.div<{ danger?: boolean }>(
|
const DropdownItemBase = styled('div', {
|
||||||
({ theme, danger }) => ({
|
shouldForwardProp: (propName) => propName !== 'danger',
|
||||||
fontSize: '14px',
|
})<{ danger?: boolean }>(({ theme, danger }) => ({
|
||||||
whiteSpace: 'nowrap',
|
fontSize: '14px',
|
||||||
color: danger ? theme.colors.lightRed : theme.colors.primary,
|
whiteSpace: 'nowrap',
|
||||||
padding: '8px 12px',
|
color: danger ? theme.colors.lightRed : theme.colors.primary,
|
||||||
borderRadius: theme.border.radius,
|
padding: '8px 12px',
|
||||||
|
borderRadius: theme.border.radius,
|
||||||
|
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
backgroundColor: theme.colors.background,
|
backgroundColor: theme.colors.background,
|
||||||
},
|
},
|
||||||
})
|
}));
|
||||||
);
|
|
||||||
|
|
||||||
const DropdownItemButton = styled(DropdownItemBase)({
|
const DropdownItemButton = styled(DropdownItemBase)({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
|
|||||||
@@ -1,12 +1,20 @@
|
|||||||
|
import styled from '@emotion/styled';
|
||||||
import { ChangeEvent, InputHTMLAttributes, useState } from 'react';
|
import { ChangeEvent, InputHTMLAttributes, useState } from 'react';
|
||||||
import FormField from '~/components/common/form/_form_field';
|
import FormField from '~/components/common/form/_form_field';
|
||||||
import Input from '~/components/common/form/_input';
|
import Input from '~/components/common/form/_input';
|
||||||
|
|
||||||
|
// TODO: create a global style variable (fontSize)
|
||||||
|
const InputLegend = styled.p(({ theme }) => ({
|
||||||
|
fontSize: '12px',
|
||||||
|
color: theme.colors.lightRed,
|
||||||
|
}));
|
||||||
|
|
||||||
interface InputProps
|
interface InputProps
|
||||||
extends Omit<InputHTMLAttributes<HTMLInputElement>, 'onChange'> {
|
extends Omit<InputHTMLAttributes<HTMLInputElement>, 'onChange'> {
|
||||||
label: string;
|
label: string;
|
||||||
name: string;
|
name: string;
|
||||||
value?: string;
|
value?: string;
|
||||||
|
errors?: string[];
|
||||||
onChange?: (name: string, value: string) => void;
|
onChange?: (name: string, value: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -14,6 +22,7 @@ export default function TextBox({
|
|||||||
name,
|
name,
|
||||||
label,
|
label,
|
||||||
value = '',
|
value = '',
|
||||||
|
errors = [],
|
||||||
onChange,
|
onChange,
|
||||||
required = false,
|
required = false,
|
||||||
...props
|
...props
|
||||||
@@ -39,6 +48,8 @@ export default function TextBox({
|
|||||||
value={inputValue}
|
value={inputValue}
|
||||||
placeholder={props.placeholder ?? 'Type something...'}
|
placeholder={props.placeholder ?? 'Type something...'}
|
||||||
/>
|
/>
|
||||||
|
{errors.length > 0 &&
|
||||||
|
errors.map((error) => <InputLegend>{error}</InputLegend>)}
|
||||||
</FormField>
|
</FormField>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ export default function CollectionContainer({
|
|||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
<CollectionHeader />
|
<CollectionHeader />
|
||||||
<CollectionControls />
|
<CollectionControls collectionId={activeCollection.id} />
|
||||||
</CollectionHeaderWrapper>
|
</CollectionHeaderWrapper>
|
||||||
<CollectionDescription />
|
<CollectionDescription />
|
||||||
<LinkList links={activeCollection.links} />
|
<LinkList links={activeCollection.links} />
|
||||||
|
|||||||
@@ -1,20 +1,31 @@
|
|||||||
import PATHS from '#constants/paths';
|
import PATHS from '#constants/paths';
|
||||||
|
import type Collection from '#models/collection';
|
||||||
import { BsThreeDotsVertical } from 'react-icons/bs';
|
import { BsThreeDotsVertical } from 'react-icons/bs';
|
||||||
import { HiOutlinePencil } from 'react-icons/hi2';
|
import { GoPencil } from 'react-icons/go';
|
||||||
import { IoIosAddCircleOutline } from 'react-icons/io';
|
import { IoIosAddCircleOutline } from 'react-icons/io';
|
||||||
import { IoTrashOutline } from 'react-icons/io5';
|
import { IoTrashOutline } from 'react-icons/io5';
|
||||||
import Dropdown from '~/components/common/dropdown/dropdown';
|
import Dropdown from '~/components/common/dropdown/dropdown';
|
||||||
import { DropdownItemLink } from '~/components/common/dropdown/dropdown_item';
|
import { DropdownItemLink } from '~/components/common/dropdown/dropdown_item';
|
||||||
|
import { appendCollectionId } from '~/lib/navigation';
|
||||||
|
|
||||||
const CollectionControls = () => (
|
const CollectionControls = ({
|
||||||
|
collectionId,
|
||||||
|
}: {
|
||||||
|
collectionId: Collection['id'];
|
||||||
|
}) => (
|
||||||
<Dropdown label={<BsThreeDotsVertical />} svgSize={18}>
|
<Dropdown label={<BsThreeDotsVertical />} svgSize={18}>
|
||||||
<DropdownItemLink href={PATHS.LINK.CREATE}>
|
<DropdownItemLink href={PATHS.LINK.CREATE}>
|
||||||
<IoIosAddCircleOutline /> Add
|
<IoIosAddCircleOutline /> Add
|
||||||
</DropdownItemLink>
|
</DropdownItemLink>
|
||||||
<DropdownItemLink href={PATHS.COLLECTION.EDIT}>
|
<DropdownItemLink
|
||||||
<HiOutlinePencil /> Edit
|
href={appendCollectionId(PATHS.COLLECTION.EDIT, collectionId)}
|
||||||
|
>
|
||||||
|
<GoPencil /> Edit
|
||||||
</DropdownItemLink>
|
</DropdownItemLink>
|
||||||
<DropdownItemLink href={PATHS.COLLECTION.REMOVE} danger>
|
<DropdownItemLink
|
||||||
|
href={appendCollectionId(PATHS.COLLECTION.REMOVE, collectionId)}
|
||||||
|
danger
|
||||||
|
>
|
||||||
<IoTrashOutline /> Delete
|
<IoTrashOutline /> Delete
|
||||||
</DropdownItemLink>
|
</DropdownItemLink>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import styled from '@emotion/styled';
|
|||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { AiFillStar, AiOutlineStar } from 'react-icons/ai';
|
import { AiFillStar, AiOutlineStar } from 'react-icons/ai';
|
||||||
import { BsThreeDotsVertical } from 'react-icons/bs';
|
import { BsThreeDotsVertical } from 'react-icons/bs';
|
||||||
import { HiOutlinePencil } from 'react-icons/hi2';
|
import { GoPencil } from 'react-icons/go';
|
||||||
import { IoTrashOutline } from 'react-icons/io5';
|
import { IoTrashOutline } from 'react-icons/io5';
|
||||||
import Dropdown from '~/components/common/dropdown/dropdown';
|
import Dropdown from '~/components/common/dropdown/dropdown';
|
||||||
import {
|
import {
|
||||||
@@ -82,7 +82,7 @@ export default function LinkControls({ link }: { link: Link }) {
|
|||||||
<DropdownItemLink
|
<DropdownItemLink
|
||||||
href={appendCollectionId(PATHS.LINK.EDIT, link.collectionId)}
|
href={appendCollectionId(PATHS.LINK.EDIT, link.collectionId)}
|
||||||
>
|
>
|
||||||
<HiOutlinePencil /> Edit
|
<GoPencil /> Edit
|
||||||
</DropdownItemLink>
|
</DropdownItemLink>
|
||||||
<DropdownItemLink
|
<DropdownItemLink
|
||||||
href={appendCollectionId(PATHS.LINK.REMOVE, link.collectionId)}
|
href={appendCollectionId(PATHS.LINK.REMOVE, link.collectionId)}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export default function FormCollection({
|
|||||||
canSubmit,
|
canSubmit,
|
||||||
disableHomeLink,
|
disableHomeLink,
|
||||||
data,
|
data,
|
||||||
|
errors,
|
||||||
|
|
||||||
setData,
|
setData,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
@@ -24,6 +25,7 @@ export default function FormCollection({
|
|||||||
canSubmit: boolean;
|
canSubmit: boolean;
|
||||||
disableHomeLink?: boolean;
|
disableHomeLink?: boolean;
|
||||||
data: FormCollectionData;
|
data: FormCollectionData;
|
||||||
|
errors?: Record<string, Array<string>>;
|
||||||
|
|
||||||
setData: (name: string, value: string) => void;
|
setData: (name: string, value: string) => void;
|
||||||
handleSubmit: () => void;
|
handleSubmit: () => void;
|
||||||
@@ -53,6 +55,7 @@ export default function FormCollection({
|
|||||||
name="name"
|
name="name"
|
||||||
onChange={setData}
|
onChange={setData}
|
||||||
value={data.name}
|
value={data.name}
|
||||||
|
errors={errors?.name}
|
||||||
required
|
required
|
||||||
autoFocus
|
autoFocus
|
||||||
/>
|
/>
|
||||||
@@ -62,13 +65,14 @@ export default function FormCollection({
|
|||||||
name="description"
|
name="description"
|
||||||
onChange={setData}
|
onChange={setData}
|
||||||
value={data.description ?? undefined}
|
value={data.description ?? undefined}
|
||||||
|
errors={errors?.description}
|
||||||
/>
|
/>
|
||||||
<FormField>
|
<FormField>
|
||||||
<label htmlFor="visibility">Public</label>
|
<label htmlFor="visibility">Public</label>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
onChange={handleOnCheck}
|
onChange={handleOnCheck}
|
||||||
value={data.visibility}
|
checked={data.visibility === Visibility.PUBLIC}
|
||||||
id="visibility"
|
id="visibility"
|
||||||
/>
|
/>
|
||||||
</FormField>
|
</FormField>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import BaseLayout from './_base_layout';
|
|||||||
|
|
||||||
const DashboardLayoutStyle = styled.div(({ theme }) => ({
|
const DashboardLayoutStyle = styled.div(({ theme }) => ({
|
||||||
height: '100%',
|
height: '100%',
|
||||||
width: theme.media.small_desktop,
|
width: theme.media.medium_desktop,
|
||||||
maxWidth: '100%',
|
maxWidth: '100%',
|
||||||
padding: '0.75em 1em',
|
padding: '0.75em 1em',
|
||||||
}));
|
}));
|
||||||
|
|||||||
41
inertia/pages/collections/edit.tsx
Normal file
41
inertia/pages/collections/edit.tsx
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import type Collection from '#models/collection';
|
||||||
|
import { useForm } from '@inertiajs/react';
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
import FormCollection, {
|
||||||
|
FormCollectionData,
|
||||||
|
} from '~/components/form/form_collection';
|
||||||
|
|
||||||
|
export default function EditCollectionPage({
|
||||||
|
collection,
|
||||||
|
}: {
|
||||||
|
collection: Collection;
|
||||||
|
}) {
|
||||||
|
const { data, setData, put, processing, errors } =
|
||||||
|
useForm<FormCollectionData>({
|
||||||
|
name: collection.name,
|
||||||
|
description: collection.description,
|
||||||
|
visibility: collection.visibility,
|
||||||
|
});
|
||||||
|
const canSubmit = useMemo<boolean>(() => {
|
||||||
|
const isFormEdited =
|
||||||
|
data.name !== collection.name ||
|
||||||
|
data.description !== collection.description ||
|
||||||
|
data.visibility !== collection.visibility;
|
||||||
|
const isFormValid = data.name !== '';
|
||||||
|
return isFormEdited && isFormValid && !processing;
|
||||||
|
}, [data, collection]);
|
||||||
|
|
||||||
|
const handleSubmit = () => put(`/collections/${collection.id}`);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormCollection
|
||||||
|
title="Edit a collection"
|
||||||
|
canSubmit={canSubmit}
|
||||||
|
data={data}
|
||||||
|
setData={setData}
|
||||||
|
handleSubmit={handleSubmit}
|
||||||
|
// TODO: fix this, type mistmatch (Record<string, string[]> sent by the backend, but useForm expects a Record<string, string>)
|
||||||
|
errors={errors as any}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -27,6 +27,9 @@ router
|
|||||||
]);
|
]);
|
||||||
router.post('/collections', [CollectionsController, 'store']);
|
router.post('/collections', [CollectionsController, 'store']);
|
||||||
|
|
||||||
|
router.get(PATHS.COLLECTION.EDIT, [CollectionsController, 'showEditPage']);
|
||||||
|
router.put('/collections/:id', [CollectionsController, 'update']);
|
||||||
|
|
||||||
router.get(PATHS.LINK.CREATE, [LinksController, 'showCreatePage']);
|
router.get(PATHS.LINK.CREATE, [LinksController, 'showCreatePage']);
|
||||||
router.post('/links', [LinksController, 'store']);
|
router.post('/links', [LinksController, 'store']);
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user