Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: create the service for the creation of links #26

Merged
merged 4 commits into from
Aug 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion client/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Routes } from './Routes';
import { AuthProvider } from './context/AuthContext';
import { LinksProvider } from './context/LinxContext';
import { LinksProvider } from './context/LinksContext';
import { ThemeProvider } from './context/ThemeContext';

function App() {
Expand Down
2 changes: 1 addition & 1 deletion client/src/Routes.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Routes as ReactRoutes, Route } from 'react-router';
import useAuth from './hooks/Auth';
import useAuth from './hooks/useAuth';
import HomeApp from './pages/(app)/Home';
import Login from './pages/(auth)/Login';
import Signup from './pages/(auth)/Signup';
Expand Down
6 changes: 4 additions & 2 deletions client/src/components/datagrids/links/links-table-toolbar.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { Cross2Icon } from '@radix-ui/react-icons';
import { Cross2Icon, Link1Icon } from '@radix-ui/react-icons';
import type { Table } from '@tanstack/react-table';

import { Button } from '../../ui/button';
import { Input } from '../../ui/input';
import { DataTableViewOptions } from './links-table-view-options';
import { LinkModalCreate } from './modal-create';

interface DataTableToolbarProps<TData> {
table: Table<TData>;
Expand All @@ -29,7 +30,7 @@ export function DataTableToolbar<TData>({
/>
{/*
todo: for enums of data
{table.getColumn('shorter_name') && (
{table.getColumn('shorter_name') && (
<DataTableFacetedFilter
column={table.getColumn('shorter_name')}
title="shorter_name"
Expand All @@ -49,6 +50,7 @@ export function DataTableToolbar<TData>({
)}
</div>
<DataTableViewOptions table={table} />
<LinkModalCreate />
</div>
);
}
83 changes: 83 additions & 0 deletions client/src/components/datagrids/links/modal-create.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { TextField } from '@/components/text-field';
import { Button } from '@/components/ui/button';
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from '@/components/ui/dialog';
import useLinks from '@/hooks/useLinks';
import { LinkForCreateSchema } from '@linx/shared';
import { Link1Icon } from '@radix-ui/react-icons';
import { useState } from 'react';

import type { FormEvent } from 'react';

export function LinkModalCreate() {
const [shorterName, setShorterName] = useState('');
const [url, setUrl] = useState('');
const { create } = useLinks();

async function handleSubmit(event: FormEvent) {
event.preventDefault();
const dto = LinkForCreateSchema.parse({
shorter_name: shorterName,
url,
});
console.log;

create(dto);
}

return (
<Dialog>
<DialogTrigger asChild>
<Button className="ml-2 h-8 px-2 lg:px-3">
<Link1Icon className="mr-2 h-4 w-4" />
Create
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>Create link</DialogTitle>
<DialogDescription>
Make changes to your profile here. Click save when you're done.
</DialogDescription>
</DialogHeader>
<div>
<form id="create-link" className="space-y-4" onSubmit={handleSubmit}>
<TextField
label="Shorter Name"
type="text"
id="shorter_name"
name="shorter_name"
placeholder="linx-short-url"
autoComplete="off"
onValue={setShorterName}
required
/>
<TextField
label="Redirect URL"
type="url"
id="url"
name="url"
placeholder="https://example.com/longlonglonglonglonglongurl"
onValue={setUrl}
required
/>
</form>
</div>
<DialogFooter>
<DialogTrigger asChild>
<Button form="create-link" type="submit">
Create Link
</Button>
</DialogTrigger>
</DialogFooter>
</DialogContent>
</Dialog>
);
}
2 changes: 1 addition & 1 deletion client/src/components/user-nav.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import useAuth from '@/hooks/Auth';
import useAuth from '@/hooks/useAuth';
import { Avatar, AvatarFallback, AvatarImage } from '@radix-ui/react-avatar';
import { Button } from './ui/button';
import {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import type { ILink } from '@linx/shared';
import { useContext } from 'react';

import type { ILink, ILinkForCreate } from '@linx/shared';
import { createContext, useEffect, useState } from 'react';
import Services from '../services';
import { AuthContext } from './AuthContext';

export interface LinksContextProps {
isLoading: boolean;
links: ILink[] | [];
create: (link: ILinkForCreate) => void;
}

export const LinksContext = createContext<LinksContextProps | undefined>(
Expand All @@ -28,12 +30,21 @@ export const LinksProvider = ({ children }: { children?: React.ReactNode }) => {
}
};

const create = async (linkDTO: ILinkForCreate) => {
const link = await Services.link.create(linkDTO);
setLinks((oldLinks) => {
const newList = [...oldLinks];
newList.unshift(link);
return newList;
});
};

useEffect(() => {
getLinks();
}, []);

return (
<LinksContext.Provider value={{ isLoading: loading, links }}>
<LinksContext.Provider value={{ isLoading: loading, links, create }}>
{children}
</LinksContext.Provider>
);
Expand Down
File renamed without changes.
4 changes: 2 additions & 2 deletions client/src/hooks/useLinks.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { LinksContext } from '@/context/LinxContext';
import { LinksContext } from '@/context/LinksContext';
import { useContext } from 'react';

export default function useLinks() {
const context = useContext(LinksContext);
if (context === undefined) {
throw new Error('useLinks must be used in LinksProvider');
throw new Error('useLinks must be used in AuthProvider');
}
return context;
}
1 change: 0 additions & 1 deletion client/src/pages/(app)/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { DataTable } from '@/components/data-table';
import { linksColumns } from '@/components/datagrids/links/links-columns';
import { MainNav } from '@/components/main-nav';
import { UserNav } from '@/components/user-nav';

import useLinks from '@/hooks/useLinks';

export default function HomeApp() {
Expand Down
2 changes: 1 addition & 1 deletion client/src/pages/(auth)/Login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
CardHeader,
CardTitle,
} from '@/components/ui/card';
import useAuth from '../../hooks/Auth';
import useAuth from '../../hooks/useAuth';
import Services from '../../services';

export default function Login() {
Expand Down
2 changes: 1 addition & 1 deletion client/src/pages/(auth)/Signup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
import { type FormEvent, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { TextField } from '../../components/text-field';
import useAuth from '../../hooks/Auth';
import useAuth from '../../hooks/useAuth';
import Services from '../../services';

export default function Signup() {
Expand Down
21 changes: 19 additions & 2 deletions client/src/services/Link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,34 @@ import { LinkSchema } from '@linx/shared';
import { z } from 'zod';
import fetch from '../utils/fetch';

import type { ILinkForCreate } from '@linx/shared';

export default class Link {
static async getAll() {
try {
const request = await fetch('/links/getAll');

const response = await request.json();
const body = await request.json();

return z.array(LinkSchema).parse(response.data.links);
return z.array(LinkSchema).parse(body.data.links);
} catch (error) {
console.error('LinkService', error);
throw error;
}
}

static async create(link: ILinkForCreate) {
try {
const request = await fetch('/links/create', {
method: 'POST',
body: JSON.stringify(link),
});
const body = await request.json();

return LinkSchema.parse(body.data.link);
} catch (e) {
console.log('LinkService', e);
throw e;
}
}
}
2 changes: 1 addition & 1 deletion server/src/controllers/Link/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ export default async function create(request: Request, reply: Reply) {

return reply
.code(201)
.send({ success: true, message: 'Link created', data: { id: link } });
.send({ success: true, message: 'Link created', data: { link: link } });
}
17 changes: 10 additions & 7 deletions server/src/repository/Link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ import database from './database';
import type { ILink, ILinkForCreate } from '@linx/shared';

export class Link {
static async create(link: ILinkForCreate): Promise<string> {
const [id] = await database<ILink>('links').insert(link).returning('id');
return id.id;
static async create(linkDTO: ILinkForCreate): Promise<ILink> {
const [link] = await database<ILink>('links')
.insert(linkDTO)
.returning('*')
.orderBy('created_at');
return link;
}

static async getByShorterName(shorter_name: string): Promise<ILink> {
Expand All @@ -16,16 +19,16 @@ export class Link {
}

static async getByID(ID: string): Promise<ILink[]> {
const link = await database<ILink>('links')
.select('*')
.where({ user_id: ID });
const link = await database<ILink>('links').select('*').where({ id: ID });
return link;
}

static async getUserLinks(userID: string): Promise<ILink[]> {
const links = await database<ILink>('links')
.select('*')
.where({ user_id: userID });
.where({ user_id: userID })
.orderBy('created_at', 'desc');

return links;
}
}
4 changes: 2 additions & 2 deletions server/src/services/Link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { BadRequestError } from '../utils/errorHandler';
import type { ILink, ILinkForCreate } from '@linx/shared';

export default class Link {
static async create(link_dto: ILinkForCreate): Promise<string> {
static async create(link_dto: ILinkForCreate): Promise<ILink> {
const check = await Repository.link.getByShorterName(link_dto.shorter_name);
if (check)
throw new BadRequestError('Exists the shorter name, please select other');
Expand All @@ -15,7 +15,7 @@ export default class Link {
}

static async getAllByUser(user_id: string): Promise<ILink[]> {
const links = await Repository.link.getByID(user_id);
const links = await Repository.link.getUserLinks(user_id);
return links;
}
}
2 changes: 1 addition & 1 deletion shared/src/interfaces/link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ export interface ILink {
export interface ILinkForCreate {
url: string;
shorter_name: string;
user_id: string;
user_id?: string;
}
Loading