refactor: create types instead of using models

This commit is contained in:
Sonny
2024-05-26 23:43:48 +02:00
committed by Sonny
parent 55cd973b1a
commit e03952de1c
28 changed files with 582 additions and 176 deletions

View File

@@ -1,4 +1,3 @@
import type Collection from '#models/collection';
import { route } from '@izzyjs/route/client';
import { useTranslation } from 'react-i18next';
import { BsThreeDotsVertical } from 'react-icons/bs';
@@ -8,6 +7,7 @@ import { IoTrashOutline } from 'react-icons/io5';
import Dropdown from '~/components/common/dropdown/dropdown';
import { DropdownItemLink } from '~/components/common/dropdown/dropdown_item';
import { appendCollectionId } from '~/lib/navigation';
import { Collection } from '~/types/app';
export default function CollectionControls({
collectionId,

View File

@@ -1,4 +1,3 @@
import type Collection from '#models/collection';
import styled from '@emotion/styled';
import { Link } from '@inertiajs/react';
import { route } from '@izzyjs/route/client';
@@ -8,6 +7,7 @@ import TextEllipsis from '~/components/common/text_ellipsis';
import { Item } from '~/components/dashboard/side_nav/nav_item';
import useActiveCollection from '~/hooks/use_active_collection';
import { appendCollectionId } from '~/lib/navigation';
import { CollectionWithLinks } from '~/types/app';
const CollectionItemStyle = styled(Item, {
shouldForwardProp: (propName) => propName !== 'isActive',
@@ -28,7 +28,7 @@ const LinksCount = styled.div(({ theme }) => ({
export default function CollectionItem({
collection,
}: {
collection: Collection;
collection: CollectionWithLinks;
}) {
const itemRef = useRef<HTMLDivElement>(null);
const { activeCollection } = useActiveCollection();

View File

@@ -1,4 +1,3 @@
import type Link from '#models/link';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { route } from '@izzyjs/route/client';
@@ -17,6 +16,7 @@ import useActiveCollection from '~/hooks/use_active_collection';
import useCollections from '~/hooks/use_collections';
import { appendLinkId } from '~/lib/navigation';
import { makeRequest } from '~/lib/request';
import { Link } from '~/types/app';
const StartItem = styled(DropdownItemButton)(({ theme }) => ({
color: theme.colors.yellow,
@@ -54,7 +54,7 @@ export default function LinkControls({ link }: { link: Link }) {
const onFavorite = () => {
const { url, method } = route('link.toggle-favorite', {
params: { id: link.id },
params: { id: link.id.toString() },
});
makeRequest({
url,

View File

@@ -1,9 +1,9 @@
import type Link from '#models/link';
import styled from '@emotion/styled';
import { AiFillStar } from 'react-icons/ai';
import ExternalLink from '~/components/common/external_link';
import LinkControls from '~/components/dashboard/link/link_controls';
import LinkFavicon from '~/components/dashboard/link/link_favicon';
import { Link } from '~/types/app';
const LinkWrapper = styled.li(({ theme }) => ({
userSelect: 'none',

View File

@@ -1,8 +1,8 @@
import type Link from '#models/link';
import styled from '@emotion/styled';
import LinkItem from '~/components/dashboard/link/link_item';
import { NoLink } from '~/components/dashboard/link/no_item';
import { sortByCreationDate } from '~/lib/array';
import { Link } from '~/types/app';
const LinkListStyle = styled.ul({
height: '100%',

View File

@@ -1,22 +0,0 @@
import styled from '@emotion/styled';
const ActionStyle = styled.div(({ theme }) => ({
border: '0 !important',
margin: '0 !important',
padding: '0 !important',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
'& svg': {
height: '20px',
width: '20px',
transition: theme.transition.delay,
},
'&:hover svg': {
transform: 'scale(1.25)',
},
}));
export default ActionStyle;

View File

@@ -1,55 +0,0 @@
import type Collection from '#models/collection';
import type Link from '#models/link';
import { Link as LinkTag } from '@inertiajs/react';
import { MouseEventHandler } from 'react';
import { useTranslation } from 'react-i18next';
import { AiOutlineEdit } from 'react-icons/ai';
import { CgTrashEmpty } from 'react-icons/cg';
import { IoAddOutline } from 'react-icons/io5';
import ActionStyle from '~/components/dashboard/quick_action/_quick_action_style';
import { appendCollectionId, appendResourceId } from '~/lib/navigation';
type Resource = 'collection' | 'link';
type Action = 'create' | 'edit' | 'remove';
const getIconFromAction = (action: Action) => {
switch (action) {
case 'create':
return IoAddOutline;
case 'edit':
return AiOutlineEdit;
case 'remove':
return CgTrashEmpty;
}
};
const QuickResourceActionStyle = ActionStyle.withComponent(LinkTag);
export default function QuickResourceAction({
resource,
action,
collectionId,
resourceId,
onClick,
}: {
resource: Resource;
action: Action;
collectionId?: Collection['id'];
resourceId?: Collection['id'] | Link['id'];
onClick?: MouseEventHandler<HTMLAnchorElement>;
}) {
const { t } = useTranslation('common');
const ActionIcon = getIconFromAction(action);
return (
<QuickResourceActionStyle
href={appendCollectionId(
appendResourceId(`/${resource}/${action}`, resourceId),
collectionId
)}
title={t(`${resource}.${action}`)}
onClick={onClick ? (event) => onClick(event) : undefined}
>
<ActionIcon />
</QuickResourceActionStyle>
);
}

View File

@@ -1,26 +0,0 @@
import styled from '@emotion/styled';
import { MouseEventHandler } from 'react';
import { AiFillStar, AiOutlineStar } from 'react-icons/ai';
import ActionStyle from '~/components/dashboard/quick_action/_quick_action_style';
const StartIcon = styled(AiFillStar)(({ theme }) => ({
color: theme.colors.yellow,
}));
const UnstaredIcon = StartIcon.withComponent(AiOutlineStar);
const QuickLinkFavoriteStyle = ActionStyle.withComponent('div');
const QuickLinkFavorite = ({
isFavorite,
onClick,
}: {
isFavorite?: boolean;
onClick: MouseEventHandler<HTMLDivElement>;
}) => (
<QuickLinkFavoriteStyle
onClick={onClick ? (event) => onClick(event) : undefined}
>
{isFavorite ? <StartIcon /> : <UnstaredIcon />}
</QuickLinkFavoriteStyle>
);
export default QuickLinkFavorite;

View File

@@ -1,11 +1,11 @@
import { Visibility } from '#enums/visibility';
import type Collection from '#models/collection';
import { FormEvent } from 'react';
import { useTranslation } from 'react-i18next';
import Checkbox from '~/components/common/form/checkbox';
import TextBox from '~/components/common/form/textbox';
import BackToDashboard from '~/components/common/navigation/back_to_dashboard';
import FormLayout from '~/components/layouts/form_layout';
import { Collection } from '~/types/app';
export type FormCollectionData = {
name: string;

View File

@@ -1,4 +1,3 @@
import type Collection from '#models/collection';
import { FormEvent } from 'react';
import { useTranslation } from 'react-i18next';
import Checkbox from '~/components/common/form/checkbox';
@@ -6,6 +5,7 @@ import TextBox from '~/components/common/form/textbox';
import BackToDashboard from '~/components/common/navigation/back_to_dashboard';
import FormLayout from '~/components/layouts/form_layout';
import useSearchParam from '~/hooks/use_search_param';
import { Collection } from '~/types/app';
export type FormLinkData = {
name: string;

View File

@@ -1,14 +1,14 @@
import type Collection from '#models/collection';
import { createContext } from 'react';
import { CollectionWithLinks } from '~/types/app';
type ActiveCollectionContextType = {
activeCollection: Collection | null;
setActiveCollection: (collection: Collection) => void;
activeCollection: CollectionWithLinks | null;
setActiveCollection: (collection: CollectionWithLinks) => void;
};
const iActiveCollectionContextState: ActiveCollectionContextType = {
activeCollection: null,
setActiveCollection: (_: Collection) => {},
setActiveCollection: (_: CollectionWithLinks) => {},
};
export const ActiveCollectionContext =

View File

@@ -1,14 +1,16 @@
import Collection from '#models/collection';
import { createContext } from 'react';
import { CollectionWithLinks } from '~/types/app';
type CollectionsContextType = {
collections: Collection[];
setCollections: (collections: Collection[]) => void | Collection[];
collections: CollectionWithLinks[];
setCollections: (
collections: CollectionWithLinks[]
) => void | CollectionWithLinks[];
};
const iCollectionsContextState: CollectionsContextType = {
collections: [] as Collection[],
setCollections: (_: Collection[]) => {},
collections: [] as CollectionWithLinks[],
setCollections: (_: CollectionWithLinks[]) => {},
};
const CollectionsContext = createContext<CollectionsContextType>(

View File

@@ -1,5 +1,5 @@
import type Link from '#models/link';
import { createContext } from 'react';
import { Link } from '~/types/app';
type FavoritesContextType = {
favorites: Link[];

View File

@@ -1,11 +1,11 @@
import type Collection from '#models/collection';
import { CollectionWithLinks } from '~/types/app';
export default function sortCcollectionsByNextId(
collections: Collection[]
): Collection[] {
const sortedCollections: Collection[] = [];
collections: CollectionWithLinks[]
): CollectionWithLinks[] {
const sortedCollections: CollectionWithLinks[] = [];
const visit = (collection: Collection) => {
const visit = (collection: CollectionWithLinks) => {
// Check if the collection has been visited
if (sortedCollections.includes(collection)) {
return;

View File

@@ -1,5 +1,4 @@
import type Collection from '#models/collection';
import type Link from '#models/link';
import { Collection, Link } from '~/types/app';
export const appendCollectionId = (
url: string,
@@ -11,8 +10,10 @@ export const appendLinkId = (
linkId?: Link['id'] | null | undefined
) => `${url}${linkId ? `?linkId=${linkId}` : ''}`;
export const appendResourceId = (url: string, resourceId?: string) =>
`${url}${resourceId ? `/${resourceId}` : ''}`;
export const appendResourceId = (
url: string,
resourceId?: Collection['id'] | Link['id']
) => `${url}${resourceId ? `/${resourceId}` : ''}`;
export function isValidHttpUrl(urlParam: string) {
let url;

View File

@@ -1,10 +1,10 @@
import type Collection from '#models/collection';
import { useForm } from '@inertiajs/react';
import { route } from '@izzyjs/route/client';
import { useTranslation } from 'react-i18next';
import FormCollection, {
FormCollectionData,
} from '~/components/form/form_collection';
import { Collection } from '~/types/app';
export default function DeleteCollectionPage({
collection,

View File

@@ -1,4 +1,3 @@
import type Collection from '#models/collection';
import { useForm } from '@inertiajs/react';
import { route } from '@izzyjs/route/client';
import { useMemo } from 'react';
@@ -6,6 +5,7 @@ import { useTranslation } from 'react-i18next';
import FormCollection, {
FormCollectionData,
} from '~/components/form/form_collection';
import { Collection } from '~/types/app';
export default function EditCollectionPage({
collection,

View File

@@ -1,5 +1,3 @@
import type Collection from '#models/collection';
import Link from '#models/link';
import styled from '@emotion/styled';
import { router } from '@inertiajs/react';
import { route } from '@izzyjs/route/client';
@@ -18,10 +16,11 @@ import { useMediaQuery } from '~/hooks/use_media_query';
import useToggle from '~/hooks/use_modal';
import useShortcut from '~/hooks/use_shortcut';
import { appendCollectionId } from '~/lib/navigation';
import { CollectionWithLinks, Link } from '~/types/app';
interface DashboardPageProps {
collections: Collection[];
activeCollection: Collection;
collections: CollectionWithLinks[];
activeCollection: CollectionWithLinks;
}
const SideBar = styled.div(({ theme }) => ({
@@ -65,20 +64,21 @@ export default function DashboardPage(props: Readonly<DashboardPageProps>) {
function DashboardProviders(
props: Readonly<{
children: ReactNode;
collections: Collection[];
activeCollection: Collection;
collections: CollectionWithLinks[];
activeCollection: CollectionWithLinks;
}>
) {
const [globalHotkeysEnabled, setGlobalHotkeysEnabled] =
useState<boolean>(true);
const [collections, setCollections] = useState<Collection[]>(
const [collections, setCollections] = useState<CollectionWithLinks[]>(
props.collections
);
const [activeCollection, setActiveCollection] = useState<Collection | null>(
props.activeCollection || collections?.[0]
);
const [activeCollection, setActiveCollection] =
useState<CollectionWithLinks | null>(
props.activeCollection || collections?.[0]
);
const handleChangeCollection = (collection: Collection) => {
const handleChangeCollection = (collection: CollectionWithLinks) => {
setActiveCollection(collection);
router.visit(appendCollectionId(route('dashboard').url, collection.id));
};

View File

@@ -1,4 +1,3 @@
import type Collection from '#models/collection';
import { useForm } from '@inertiajs/react';
import { route } from '@izzyjs/route/client';
import { useMemo } from 'react';
@@ -6,6 +5,7 @@ import { useTranslation } from 'react-i18next';
import FormLink from '~/components/form/form_link';
import useSearchParam from '~/hooks/use_search_param';
import { isValidHttpUrl } from '~/lib/navigation';
import { Collection } from '~/types/app';
export default function CreateLinkPage({
collections,

View File

@@ -1,10 +1,10 @@
import type Link from '#models/link';
import { useForm } from '@inertiajs/react';
import { route } from '@izzyjs/route/client';
import { useTranslation } from 'react-i18next';
import FormLink from '~/components/form/form_link';
import { LinkWithCollection } from '~/types/app';
export default function DeleteLinkPage({ link }: { link: Link }) {
export default function DeleteLinkPage({ link }: { link: LinkWithCollection }) {
const { t } = useTranslation('common');
const { data, setData, submit, processing } = useForm({
name: link.name,

View File

@@ -1,11 +1,10 @@
import type Collection from '#models/collection';
import type Link from '#models/link';
import { useForm } from '@inertiajs/react';
import { route } from '@izzyjs/route/client';
import { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import FormLink from '~/components/form/form_link';
import { isValidHttpUrl } from '~/lib/navigation';
import { Collection, Link } from '~/types/app';
export default function EditLinkPage({
collections,

View File

@@ -1,26 +1,55 @@
import type Collection from '#models/collection';
import { Visibility } from '@/app/enums/visibility';
type User = {
type CommonBase = {
id: number;
createdAt: string;
updatedAt: string;
};
type User = CommonBase & {
email: string;
name: string;
nickName: string;
fullname: string;
avatarUrl: string;
isAdmin: boolean;
};
type UserWithCollections = User & {
collections: Collection[];
};
type UserWithRelationCount = {
id: number;
type UserWithRelationCount = CommonBase & {
email: string;
fullname: string;
avatarUrl: string;
isAdmin: string;
createdAt: string;
updatedAt: string;
count: {
link: number;
collection: number;
};
};
type Link = CommonBase & {
name: string;
description: string | null;
url: string;
favorite: boolean;
collectionId: number;
};
type LinkWithCollection = Link & {
collection: Collection;
};
type Collection = CommonBase & {
name: string;
description: string | null;
visibility: Visibility;
nextId: number;
authorId: number;
};
type CollectionWithLinks = Collection & {
links: Link[];
};