From a6452366f2f09ed092866334752096c975aac68b Mon Sep 17 00:00:00 2001 From: TZGyn Date: Mon, 20 Nov 2023 09:02:29 +0800 Subject: [PATCH] delete unused developments --- README.md | 23 -- docker-compose.yml | 15 - elysia/.dockerignore | 10 - elysia/.env.example | 5 - elysia/.gitignore | 43 --- elysia/.prettierrc.yaml | 15 - elysia/Dockerfile | 16 - elysia/README.md | 15 - elysia/bun.lockb | Bin 25600 -> 0 bytes elysia/docker-compose.yml | 15 - elysia/package.json | 22 -- elysia/src/auth.ts | 53 --- elysia/src/database.ts | 18 - elysia/src/index.ts | 187 ---------- elysia/src/types.ts | 50 --- elysia/src/zodSchema.ts | 12 - elysia/tsconfig.json | 105 ------ react-frontend/.dockerignore | 10 - react-frontend/.env.example | 1 - react-frontend/.eslintrc.cjs | 18 - react-frontend/.gitignore | 27 -- react-frontend/.prettierrc.yaml | 16 - react-frontend/Dockerfile | 15 - react-frontend/README.md | 27 -- react-frontend/bun.lockb | Bin 151421 -> 0 bytes react-frontend/components.json | 16 - react-frontend/index.html | 20 -- react-frontend/package.json | 48 --- react-frontend/postcss.config.js | 6 - react-frontend/public/react.svg | 1 - react-frontend/public/vite.svg | 1 - react-frontend/src/App.css | 42 --- react-frontend/src/App.tsx | 325 ------------------ react-frontend/src/components/mode-toggle.tsx | 39 --- .../src/components/theme-provider.tsx | 76 ---- .../src/components/ui/alert-dialog.tsx | 143 -------- react-frontend/src/components/ui/avatar.tsx | 48 --- react-frontend/src/components/ui/button.tsx | 56 --- react-frontend/src/components/ui/card.tsx | 79 ----- react-frontend/src/components/ui/dialog.tsx | 121 ------- .../src/components/ui/dropdown-menu.tsx | 198 ----------- react-frontend/src/components/ui/input.tsx | 25 -- react-frontend/src/components/ui/label.tsx | 24 -- react-frontend/src/components/ui/table.tsx | 114 ------ react-frontend/src/components/ui/toast.tsx | 127 ------- react-frontend/src/components/ui/toaster.tsx | 33 -- react-frontend/src/components/ui/use-toast.ts | 192 ----------- react-frontend/src/index.css | 76 ---- react-frontend/src/lib/utils.ts | 6 - react-frontend/src/main.tsx | 37 -- react-frontend/src/pages/Layout.tsx | 14 - react-frontend/src/pages/dashboard.tsx | 270 --------------- react-frontend/src/vite-env.d.ts | 1 - react-frontend/tailwind.config.js | 76 ---- react-frontend/tsconfig.json | 29 -- react-frontend/tsconfig.node.json | 10 - react-frontend/vite.config.ts | 12 - vue-frontend/.env | 2 - vue-frontend/.gitignore | 28 -- vue-frontend/.prettierrc.yaml | 16 - vue-frontend/.vscode/extensions.json | 3 - vue-frontend/Dockerfile | 15 - vue-frontend/README.md | 40 --- vue-frontend/bun.lockb | Bin 191494 -> 0 bytes vue-frontend/components.json | 15 - vue-frontend/docker-compose.yml | 15 - vue-frontend/env.d.ts | 1 - vue-frontend/index.html | 13 - vue-frontend/package.json | 40 --- vue-frontend/postcss.config.js | 6 - vue-frontend/public/favicon.ico | Bin 4286 -> 0 bytes vue-frontend/src/App.vue | 11 - vue-frontend/src/assets/index.css | 80 ----- vue-frontend/src/assets/logo.svg | 1 - vue-frontend/src/components/Navbar.vue | 21 -- .../src/components/ToggleThemeButton.vue | 21 -- vue-frontend/src/components/UserLoginForm.vue | 94 ----- .../src/components/UserSignUpForm.vue | 117 ------- .../src/components/ui/button/Button.vue | 23 -- .../src/components/ui/button/index.ts | 33 -- .../src/components/ui/input/Input.vue | 22 -- vue-frontend/src/components/ui/input/index.ts | 1 - .../src/components/ui/label/Label.vue | 20 -- vue-frontend/src/components/ui/label/index.ts | 1 - vue-frontend/src/lib/auth.ts | 3 - vue-frontend/src/lib/fetch.ts | 7 - vue-frontend/src/lib/utils.ts | 7 - vue-frontend/src/main.ts | 11 - vue-frontend/src/router/index.ts | 43 --- vue-frontend/src/views/HomeView.vue | 3 - vue-frontend/src/views/LoginPage.vue | 43 --- vue-frontend/src/views/SignUpPage.vue | 43 --- vue-frontend/tailwind.config.js | 79 ----- vue-frontend/tsconfig.app.json | 12 - vue-frontend/tsconfig.json | 11 - vue-frontend/tsconfig.node.json | 16 - vue-frontend/vite.config.ts | 16 - 97 files changed, 3916 deletions(-) delete mode 100644 README.md delete mode 100644 docker-compose.yml delete mode 100644 elysia/.dockerignore delete mode 100644 elysia/.env.example delete mode 100644 elysia/.gitignore delete mode 100644 elysia/.prettierrc.yaml delete mode 100644 elysia/Dockerfile delete mode 100644 elysia/README.md delete mode 100755 elysia/bun.lockb delete mode 100644 elysia/docker-compose.yml delete mode 100644 elysia/package.json delete mode 100644 elysia/src/auth.ts delete mode 100644 elysia/src/database.ts delete mode 100644 elysia/src/index.ts delete mode 100644 elysia/src/types.ts delete mode 100644 elysia/src/zodSchema.ts delete mode 100644 elysia/tsconfig.json delete mode 100644 react-frontend/.dockerignore delete mode 100644 react-frontend/.env.example delete mode 100644 react-frontend/.eslintrc.cjs delete mode 100644 react-frontend/.gitignore delete mode 100644 react-frontend/.prettierrc.yaml delete mode 100644 react-frontend/Dockerfile delete mode 100644 react-frontend/README.md delete mode 100755 react-frontend/bun.lockb delete mode 100644 react-frontend/components.json delete mode 100644 react-frontend/index.html delete mode 100644 react-frontend/package.json delete mode 100644 react-frontend/postcss.config.js delete mode 100644 react-frontend/public/react.svg delete mode 100644 react-frontend/public/vite.svg delete mode 100644 react-frontend/src/App.css delete mode 100644 react-frontend/src/App.tsx delete mode 100644 react-frontend/src/components/mode-toggle.tsx delete mode 100644 react-frontend/src/components/theme-provider.tsx delete mode 100644 react-frontend/src/components/ui/alert-dialog.tsx delete mode 100644 react-frontend/src/components/ui/avatar.tsx delete mode 100644 react-frontend/src/components/ui/button.tsx delete mode 100644 react-frontend/src/components/ui/card.tsx delete mode 100644 react-frontend/src/components/ui/dialog.tsx delete mode 100644 react-frontend/src/components/ui/dropdown-menu.tsx delete mode 100644 react-frontend/src/components/ui/input.tsx delete mode 100644 react-frontend/src/components/ui/label.tsx delete mode 100644 react-frontend/src/components/ui/table.tsx delete mode 100644 react-frontend/src/components/ui/toast.tsx delete mode 100644 react-frontend/src/components/ui/toaster.tsx delete mode 100644 react-frontend/src/components/ui/use-toast.ts delete mode 100644 react-frontend/src/index.css delete mode 100644 react-frontend/src/lib/utils.ts delete mode 100644 react-frontend/src/main.tsx delete mode 100644 react-frontend/src/pages/Layout.tsx delete mode 100644 react-frontend/src/pages/dashboard.tsx delete mode 100644 react-frontend/src/vite-env.d.ts delete mode 100644 react-frontend/tailwind.config.js delete mode 100644 react-frontend/tsconfig.json delete mode 100644 react-frontend/tsconfig.node.json delete mode 100644 react-frontend/vite.config.ts delete mode 100644 vue-frontend/.env delete mode 100644 vue-frontend/.gitignore delete mode 100644 vue-frontend/.prettierrc.yaml delete mode 100644 vue-frontend/.vscode/extensions.json delete mode 100644 vue-frontend/Dockerfile delete mode 100644 vue-frontend/README.md delete mode 100755 vue-frontend/bun.lockb delete mode 100644 vue-frontend/components.json delete mode 100644 vue-frontend/docker-compose.yml delete mode 100644 vue-frontend/env.d.ts delete mode 100644 vue-frontend/index.html delete mode 100644 vue-frontend/package.json delete mode 100644 vue-frontend/postcss.config.js delete mode 100644 vue-frontend/public/favicon.ico delete mode 100644 vue-frontend/src/App.vue delete mode 100644 vue-frontend/src/assets/index.css delete mode 100644 vue-frontend/src/assets/logo.svg delete mode 100644 vue-frontend/src/components/Navbar.vue delete mode 100644 vue-frontend/src/components/ToggleThemeButton.vue delete mode 100644 vue-frontend/src/components/UserLoginForm.vue delete mode 100644 vue-frontend/src/components/UserSignUpForm.vue delete mode 100644 vue-frontend/src/components/ui/button/Button.vue delete mode 100644 vue-frontend/src/components/ui/button/index.ts delete mode 100644 vue-frontend/src/components/ui/input/Input.vue delete mode 100644 vue-frontend/src/components/ui/input/index.ts delete mode 100644 vue-frontend/src/components/ui/label/Label.vue delete mode 100644 vue-frontend/src/components/ui/label/index.ts delete mode 100644 vue-frontend/src/lib/auth.ts delete mode 100644 vue-frontend/src/lib/fetch.ts delete mode 100644 vue-frontend/src/lib/utils.ts delete mode 100644 vue-frontend/src/main.ts delete mode 100644 vue-frontend/src/router/index.ts delete mode 100644 vue-frontend/src/views/HomeView.vue delete mode 100644 vue-frontend/src/views/LoginPage.vue delete mode 100644 vue-frontend/src/views/SignUpPage.vue delete mode 100644 vue-frontend/tailwind.config.js delete mode 100644 vue-frontend/tsconfig.app.json delete mode 100644 vue-frontend/tsconfig.json delete mode 100644 vue-frontend/tsconfig.node.json delete mode 100644 vue-frontend/vite.config.ts diff --git a/README.md b/README.md deleted file mode 100644 index d20be81..0000000 --- a/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# Shortener - -### Production - -```bash -docker-compose up -d --build -``` - -### Local Development - -Backend - -```bash -cd elysia -docker-compose up -``` - -Frontend - -```bash -cd react-frontend -bun run dev --host -``` diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index dfc7b71..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,15 +0,0 @@ ---- -version: '1.0' -services: - shortener_frontend: - container_name: shortener_frontend - build: - context: ./react-frontend - dockerfile: Dockerfile - ports: [3003:4173] - shortener_backend: - container_name: shortener_backend - build: - context: ./elysia - dockerfile: Dockerfile - ports: [3004:3000] diff --git a/elysia/.dockerignore b/elysia/.dockerignore deleted file mode 100644 index 6b38648..0000000 --- a/elysia/.dockerignore +++ /dev/null @@ -1,10 +0,0 @@ -node_modules -npm-debug.log -Dockerfile* -docker-compose* -.dockerignore -.git -.gitignore -README.md -LICENSE -.vscode diff --git a/elysia/.env.example b/elysia/.env.example deleted file mode 100644 index 2ab9166..0000000 --- a/elysia/.env.example +++ /dev/null @@ -1,5 +0,0 @@ -host=0.0.0.0 -user=postgres -password=password -port=5432 -FALLBACK_URL=https://shortener.tzgyn.com diff --git a/elysia/.gitignore b/elysia/.gitignore deleted file mode 100644 index eb291b3..0000000 --- a/elysia/.gitignore +++ /dev/null @@ -1,43 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.js - -# testing -/coverage - -# next.js -/.next/ -/out/ - -# production -/build - -# misc -.DS_Store -*.pem - -# debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# local env files -.env -.env.local -.env.development.local -.env.test.local -.env.production.local - -# vercel -.vercel - -**/*.trace -**/*.zip -**/*.tar.gz -**/*.tgz -**/*.log -package-lock.json -**/*.bun diff --git a/elysia/.prettierrc.yaml b/elysia/.prettierrc.yaml deleted file mode 100644 index 9ffa4a8..0000000 --- a/elysia/.prettierrc.yaml +++ /dev/null @@ -1,15 +0,0 @@ ---- -printWidth: 80 -tabWidth: 4 -useTabs: true -semi: false -singleQuote: true -quoteProps: consistent -jsxSingleQuote: true -trailingComma: es5 -bracketSpacing: true -bracketSameLine: true -arrowParens: always -htmlWhitespaceSensitivity: strict -vueIndentScriptAndStyle: false -singleAttributePerLine: true diff --git a/elysia/Dockerfile b/elysia/Dockerfile deleted file mode 100644 index 3fd5aff..0000000 --- a/elysia/Dockerfile +++ /dev/null @@ -1,16 +0,0 @@ -FROM docker.io/oven/bun - -RUN mkdir /shortener-backend -WORKDIR /shortener-backend - -COPY ./package.json ./ -COPY ./bun.lockb ./ - -RUN bun install --production - -COPY . . - -EXPOSE 3000 -ENV NODE_ENV production - -ENTRYPOINT [ "bun", "run", "./src/index.ts" ] diff --git a/elysia/README.md b/elysia/README.md deleted file mode 100644 index 688c87e..0000000 --- a/elysia/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# Elysia with Bun runtime - -## Getting Started -To get started with this template, simply paste this command into your terminal: -```bash -bun create elysia ./elysia-example -``` - -## Development -To start the development server run: -```bash -bun run dev -``` - -Open http://localhost:3000/ with your browser to see the result. \ No newline at end of file diff --git a/elysia/bun.lockb b/elysia/bun.lockb deleted file mode 100755 index f62cded69969f328893d8041237cf11e46037622..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25600 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 H@9%#BX{54- diff --git a/elysia/docker-compose.yml b/elysia/docker-compose.yml deleted file mode 100644 index 3d28cf9..0000000 --- a/elysia/docker-compose.yml +++ /dev/null @@ -1,15 +0,0 @@ ---- -# docker-compose.yml -version: '3.9' -services: - app: - image: oven/bun - container_name: linkshortener_elysia_dev - # override default entrypoint allows us to do `bun install` before serving - entrypoint: [] - # execute bun install before we start the dev server in watch mode - command: /bin/sh -c 'bun install && bun run --watch src/index.ts' - # expose the right ports - ports: [3000:3000] - # setup a host mounted volume to sync changes to the container - volumes: [./:/home/bun/app] diff --git a/elysia/package.json b/elysia/package.json deleted file mode 100644 index 2b0287d..0000000 --- a/elysia/package.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "elysia", - "version": "1.0.50", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "dev": "bun run --watch src/index.ts" - }, - "dependencies": { - "@elysiajs/cors": "^0.6.0", - "@types/pg": "^8.10.2", - "elysia": "latest", - "kysely": "^0.26.3", - "magic-regexp": "^0.7.0", - "nanoid": "^5.0.1", - "pg": "^8.11.3", - "zod": "^3.22.2" - }, - "devDependencies": { - "bun-types": "latest" - }, - "module": "src/index.js" -} \ No newline at end of file diff --git a/elysia/src/auth.ts b/elysia/src/auth.ts deleted file mode 100644 index 8b29a2e..0000000 --- a/elysia/src/auth.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { nanoid } from 'nanoid' -import { db } from './database' - -export const signup = async ( - email: string, - username: string, - password: string, - password_confirm: string -) => { - if (password !== password_confirm) { - return { error: 'password is not the same' } - } - - if (password.length < 8) { - return { error: 'password should be at least length 8' } - } - - try { - await db - .insertInto('user') - .values({ - uuid: nanoid(16), - email, - username, - password: await Bun.password.hash(password), - }) - .execute() - return { error: undefined } - } catch (error) { - console.log(error) - return { error: 'error' } - } -} - -export const login = async (email: string, password: string) => { - const userArray = await db - .selectFrom('user') - .selectAll() - .where('user.email', '=', email) - .execute() - - if (userArray.length < 1) { - return { error: 'Invalid User' } - } - - const user = userArray[0] - - if (await Bun.password.verify(password, user.password)) { - return { user } - } else { - return { error: 'Incorrect Credentials' } - } -} diff --git a/elysia/src/database.ts b/elysia/src/database.ts deleted file mode 100644 index 68d6de4..0000000 --- a/elysia/src/database.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Database } from './types' -import { Pool } from 'pg' -import { Kysely, PostgresDialect } from 'kysely' - -const dialect = new PostgresDialect({ - pool: new Pool({ - database: 'link-shortener', - host: Bun.env.host ?? '0.0.0.0', - user: Bun.env.user ?? 'postgres', - password: Bun.env.password ?? 'password', - port: parseInt(Bun.env.port ?? '') ?? 5432, - max: 10, - }), -}) - -export const db = new Kysely({ - dialect, -}) diff --git a/elysia/src/index.ts b/elysia/src/index.ts deleted file mode 100644 index 86218e9..0000000 --- a/elysia/src/index.ts +++ /dev/null @@ -1,187 +0,0 @@ -import { Elysia, t } from 'elysia' -import { nanoid } from 'nanoid' -import { db } from './database' -import { createLinkSchema } from './zodSchema' -import { cors } from '@elysiajs/cors' -import { jsonArrayFrom } from 'kysely/helpers/postgres' -import { login, signup } from './auth' - -const fallback_url = Bun.env.FALLBACK_URL ?? 'https://shortener.tzgyn.com' - -const app = new Elysia().use(cors()) - -app.get('/', () => 'Hello Elysia') -app.get('/invalid', () => 'Invalid Shortener') - -app.get('/link', async () => { - const shorteners = await db - .selectFrom('shortener') - .leftJoin('visitor', 'visitor.shortener_id', 'shortener.id') - .select(({ fn }) => [ - 'shortener.id', - 'shortener.link', - 'shortener.code', - 'shortener.created_at', - fn.count('visitor.id').as('visitor_count'), - ]) - .groupBy('shortener.id') - .execute() - - return { shorteners } -}) - -app.post('/link', async ({ body, set }) => { - const createLink = createLinkSchema.safeParse(body) - - if (!createLink.success) { - set.status = 400 - return { message: 'Invalid Link', body } - } - - const uuid = nanoid(10) - - await db - .insertInto('shortener') - .values({ - link: createLink.data.link, - code: uuid, - }) - .execute() - - return { message: 'Success' } -}) - -app.get( - '/:shortenerCode', - async ({ params: { shortenerCode }, set, request }) => { - const ip = request.headers.get('x-forwarded-for') - - const geolocation = await ( - await fetch(`https://api.ipbase.com/v2/info?ip=${ip}`) - ).json() - - try { - const shortener = await db - .selectFrom('shortener') - .selectAll() - .where('code', '=', shortenerCode) - .orderBy('created_at', 'desc') - .execute() - - const visitor_data = { - shortener_id: shortener[0].id, - country: geolocation.data.location.country.name as string, - country_code: geolocation.data.location.country - .alpha2 as string, - } - - await db.insertInto('visitor').values(visitor_data).execute() - - if (!shortener.length) { - set.redirect = '/invalid' - return - } - - set.redirect = shortener[0].link - } catch { - set.redirect = fallback_url - } - } -) - -app.get('/link/:shortenerCode', async ({ params: { shortenerCode } }) => { - try { - const shorteners = await db - .selectFrom('shortener') - .select((shortener) => [ - 'id', - 'code', - 'link', - 'created_at', - jsonArrayFrom( - shortener - .selectFrom('visitor') - .select([ - 'visitor.created_at as visited_at', - 'visitor.country_code', - ]) - .whereRef('visitor.shortener_id', '=', 'shortener.id') - ).as('visitors'), - ]) - .where('code', '=', shortenerCode) - .execute() - - const visitors = await db - .selectFrom('visitor') - .select(({ fn }) => [ - 'visitor.country_code', - 'visitor.country', - fn.count('visitor.id').as('visitor_count'), - ]) - .where('visitor.shortener_id', '=', shorteners[0].id) - .groupBy(['visitor.country_code', 'visitor.country']) - .execute() - - return { shorteners, visitors } - } catch { - return { error: true } - } -}) - -app.post( - '/signup', - async ({ body, set }) => { - const { email, username, password, password_confirm } = body - - const { error } = await signup( - email, - username, - password, - password_confirm - ) - - if (error) { - set.status = 400 - return { error } - } - - return { message: 'User Successfully Created' } - }, - { - body: t.Object({ - username: t.String(), - email: t.String(), - password: t.String(), - password_confirm: t.String(), - }), - } -) - -app.post( - '/login', - async ({ body, set }) => { - const { email, password } = body - const { user, error } = await login(email, password) - - if (error) { - set.status = 400 - return { error } - } else { - return user - } - }, - { - body: t.Object({ - email: t.String(), - password: t.String(), - }), - } -) - -app.listen(3000) - -console.log( - `🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}` -) - -export type App = typeof app diff --git a/elysia/src/types.ts b/elysia/src/types.ts deleted file mode 100644 index a405199..0000000 --- a/elysia/src/types.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { - ColumnType, - Generated, - Insertable, - Selectable, - Updateable, -} from 'kysely' - -export type Timestamp = ColumnType - -export interface Database { - shortener: ShortenerTable - visitor: VisitorTable - user: UserTable -} - -export interface ShortenerTable { - id: Generated - link: string - code: string - created_at: ColumnType -} - -export type Shortener = Selectable -export type NewShortener = Insertable -export type ShortenerUpdate = Updateable - -export interface VisitorTable { - id: Generated - shortener_id: number - country: string - country_code: string - created_at: ColumnType -} - -export type Visitor = Selectable -export type NewVisitor = Insertable - -export interface UserTable { - created_at: Generated - email: string - id: Generated - password: string - username: string - uuid: string -} - -export type User = Selectable -export type NewUser = Insertable -export type UserUpdate = Updateable diff --git a/elysia/src/zodSchema.ts b/elysia/src/zodSchema.ts deleted file mode 100644 index e748de8..0000000 --- a/elysia/src/zodSchema.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { z } from 'zod' -import { char, createRegExp, exactly, oneOrMore } from 'magic-regexp' - -const urlRegex = createRegExp( - exactly('https://'), - oneOrMore(oneOrMore(char), exactly('.')), - oneOrMore(char) -) - -export const createLinkSchema = z.object({ - link: z.string().regex(urlRegex), -}) diff --git a/elysia/tsconfig.json b/elysia/tsconfig.json deleted file mode 100644 index 7b732ff..0000000 --- a/elysia/tsconfig.json +++ /dev/null @@ -1,105 +0,0 @@ -{ - "compilerOptions": { - /* Visit https://aka.ms/tsconfig to read more about this file */ - - /* Projects */ - // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ - // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ - // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ - // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ - // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ - // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ - - /* Language and Environment */ - "target": "ES2021" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, - // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ - // "jsx": "preserve", /* Specify what JSX code is generated. */ - // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ - // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ - // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ - // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ - // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ - // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ - // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ - // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ - // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ - - /* Modules */ - "module": "ES2022" /* Specify what module code is generated. */, - // "rootDir": "./", /* Specify the root folder within your source files. */ - "moduleResolution": "node" /* Specify how TypeScript looks up a file from a given module specifier. */, - // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ - // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ - // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ - // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ - "types": [ - "bun-types" - ] /* Specify type package names to be included without being referenced in a source file. */, - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ - // "resolveJsonModule": true, /* Enable importing .json files. */ - // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ - - /* JavaScript Support */ - // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ - // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ - // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ - - /* Emit */ - // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ - // "declarationMap": true, /* Create sourcemaps for d.ts files. */ - // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ - // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ - // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ - // "outDir": "./", /* Specify an output folder for all emitted files. */ - // "removeComments": true, /* Disable emitting comments. */ - // "noEmit": true, /* Disable emitting files from a compilation. */ - // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ - // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ - // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ - // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ - // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ - // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ - // "newLine": "crlf", /* Set the newline character for emitting files. */ - // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ - // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ - // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ - // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ - // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ - // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ - - /* Interop Constraints */ - // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ - // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ - "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, - // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ - "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, - - /* Type Checking */ - "strict": true /* Enable all strict type-checking options. */, - "noImplicitAny": true /* Enable error reporting for expressions and declarations with an implied 'any' type. */, - // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ - // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ - // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ - // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ - // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ - // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ - // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ - // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ - // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ - // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ - // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ - // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ - // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ - // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ - // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ - // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ - // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ - - /* Completeness */ - // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ - "skipLibCheck": true /* Skip type checking all .d.ts files. */ - } -} diff --git a/react-frontend/.dockerignore b/react-frontend/.dockerignore deleted file mode 100644 index 6b38648..0000000 --- a/react-frontend/.dockerignore +++ /dev/null @@ -1,10 +0,0 @@ -node_modules -npm-debug.log -Dockerfile* -docker-compose* -.dockerignore -.git -.gitignore -README.md -LICENSE -.vscode diff --git a/react-frontend/.env.example b/react-frontend/.env.example deleted file mode 100644 index 09a53ce..0000000 --- a/react-frontend/.env.example +++ /dev/null @@ -1 +0,0 @@ -VITE_BACKEND_URL=https://s.tzgyn.com diff --git a/react-frontend/.eslintrc.cjs b/react-frontend/.eslintrc.cjs deleted file mode 100644 index d6c9537..0000000 --- a/react-frontend/.eslintrc.cjs +++ /dev/null @@ -1,18 +0,0 @@ -module.exports = { - root: true, - env: { browser: true, es2020: true }, - extends: [ - 'eslint:recommended', - 'plugin:@typescript-eslint/recommended', - 'plugin:react-hooks/recommended', - ], - ignorePatterns: ['dist', '.eslintrc.cjs'], - parser: '@typescript-eslint/parser', - plugins: ['react-refresh'], - rules: { - 'react-refresh/only-export-components': [ - 'warn', - { allowConstantExport: true }, - ], - }, -} diff --git a/react-frontend/.gitignore b/react-frontend/.gitignore deleted file mode 100644 index d79687c..0000000 --- a/react-frontend/.gitignore +++ /dev/null @@ -1,27 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* -lerna-debug.log* - -node_modules -dist -dist-ssr -*.local - -# Editor directories and files -.vscode/* -!.vscode/extensions.json -.idea -.DS_Store -*.suo -*.ntvs* -*.njsproj -*.sln -*.sw? - -# environment -.env diff --git a/react-frontend/.prettierrc.yaml b/react-frontend/.prettierrc.yaml deleted file mode 100644 index 1439e2e..0000000 --- a/react-frontend/.prettierrc.yaml +++ /dev/null @@ -1,16 +0,0 @@ ---- -printWidth: 80 -tabWidth: 4 -useTabs: true -semi: false -singleQuote: true -quoteProps: consistent -jsxSingleQuote: true -trailingComma: es5 -bracketSpacing: true -bracketSameLine: true -arrowParens: always -htmlWhitespaceSensitivity: strict -vueIndentScriptAndStyle: false -singleAttributePerLine: true -plugins: [prettier-plugin-tailwindcss] diff --git a/react-frontend/Dockerfile b/react-frontend/Dockerfile deleted file mode 100644 index 2978fce..0000000 --- a/react-frontend/Dockerfile +++ /dev/null @@ -1,15 +0,0 @@ -FROM docker.io/oven/bun - -RUN mkdir /shortener-frontend -WORKDIR /shortener-frontend - -COPY ./package.json ./ -COPY ./bun.lockb ./ - -RUN bun install - -COPY . . - -RUN bun run build - -ENTRYPOINT [ "bun", "run", "preview", "--host" ] diff --git a/react-frontend/README.md b/react-frontend/README.md deleted file mode 100644 index 1ebe379..0000000 --- a/react-frontend/README.md +++ /dev/null @@ -1,27 +0,0 @@ -# React + TypeScript + Vite - -This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. - -Currently, two official plugins are available: - -- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh -- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh - -## Expanding the ESLint configuration - -If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: - -- Configure the top-level `parserOptions` property like this: - -```js - parserOptions: { - ecmaVersion: 'latest', - sourceType: 'module', - project: ['./tsconfig.json', './tsconfig.node.json'], - tsconfigRootDir: __dirname, - }, -``` - -- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked` -- Optionally add `plugin:@typescript-eslint/stylistic-type-checked` -- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list diff --git a/react-frontend/bun.lockb b/react-frontend/bun.lockb deleted file mode 100755 index 95fa88593613fed4782fb17468941734212df4dc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 151421 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%0A%h{2# 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 nt5u#_XN@Alx{ - - - - - - Shortener - - -
- - - diff --git a/react-frontend/package.json b/react-frontend/package.json deleted file mode 100644 index d429a09..0000000 --- a/react-frontend/package.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "name": "react-frontend", - "private": true, - "version": "0.0.0", - "type": "module", - "scripts": { - "dev": "vite", - "build": "tsc && vite build", - "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", - "preview": "vite preview" - }, - "dependencies": { - "@radix-ui/react-alert-dialog": "^1.0.4", - "@radix-ui/react-avatar": "^1.0.4", - "@radix-ui/react-dialog": "^1.0.4", - "@radix-ui/react-dropdown-menu": "^2.0.5", - "@radix-ui/react-label": "^2.0.2", - "@radix-ui/react-slot": "^1.0.2", - "@radix-ui/react-toast": "^1.1.4", - "class-variance-authority": "^0.7.0", - "clsx": "^2.0.0", - "lucide-react": "^0.274.0", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "recharts": "^2.8.0", - "tailwind-merge": "^1.14.0", - "tailwindcss-animate": "^1.0.7" - }, - "devDependencies": { - "@types/node": "^20.6.0", - "@types/react": "^18.2.15", - "@types/react-dom": "^18.2.7", - "@typescript-eslint/eslint-plugin": "^6.0.0", - "@typescript-eslint/parser": "^6.0.0", - "@vitejs/plugin-react": "^4.0.3", - "autoprefixer": "^10.4.15", - "eslint": "^8.45.0", - "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-react-refresh": "^0.4.3", - "postcss": "^8.4.29", - "prettier": "^3.0.3", - "prettier-plugin-tailwindcss": "^0.5.4", - "react-router-dom": "^6.16.0", - "tailwindcss": "^3.3.3", - "typescript": "^5.0.2", - "vite": "^4.4.5" - } -} diff --git a/react-frontend/postcss.config.js b/react-frontend/postcss.config.js deleted file mode 100644 index 2e7af2b..0000000 --- a/react-frontend/postcss.config.js +++ /dev/null @@ -1,6 +0,0 @@ -export default { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - }, -} diff --git a/react-frontend/public/react.svg b/react-frontend/public/react.svg deleted file mode 100644 index 6c87de9..0000000 --- a/react-frontend/public/react.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/react-frontend/public/vite.svg b/react-frontend/public/vite.svg deleted file mode 100644 index e7b8dfb..0000000 --- a/react-frontend/public/vite.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/react-frontend/src/App.css b/react-frontend/src/App.css deleted file mode 100644 index b9d355d..0000000 --- a/react-frontend/src/App.css +++ /dev/null @@ -1,42 +0,0 @@ -#root { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; -} - -.logo { - height: 6em; - padding: 1.5em; - will-change: filter; - transition: filter 300ms; -} -.logo:hover { - filter: drop-shadow(0 0 2em #646cffaa); -} -.logo.react:hover { - filter: drop-shadow(0 0 2em #61dafbaa); -} - -@keyframes logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} - -@media (prefers-reduced-motion: no-preference) { - a:nth-of-type(2) .logo { - animation: logo-spin infinite 20s linear; - } -} - -.card { - padding: 2em; -} - -.read-the-docs { - color: #888; -} diff --git a/react-frontend/src/App.tsx b/react-frontend/src/App.tsx deleted file mode 100644 index e486d0a..0000000 --- a/react-frontend/src/App.tsx +++ /dev/null @@ -1,325 +0,0 @@ -import { ModeToggle } from '@/components/mode-toggle' -import { - Dialog, - DialogContent, - DialogDescription, - DialogHeader, - DialogFooter, - DialogTitle, - DialogTrigger, -} from '@/components/ui/dialog' -import { Button } from '@/components/ui/button' -import { Input } from '@/components/ui/input' -import { Label } from '@/components/ui/label' -import { - Table, - TableBody, - TableCaption, - TableCell, - TableHead, - TableHeader, - TableRow, -} from '@/components/ui/table' -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuLabel, - DropdownMenuSeparator, - DropdownMenuTrigger, -} from '@/components/ui/dropdown-menu' -import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar' -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' -import { Toaster } from '@/components/ui/toaster' -import { useToast } from '@/components/ui/use-toast' - -import { - Check, - Copy, - Loader2, - MoreHorizontal, - Settings, - User, -} from 'lucide-react' - -import { useEffect, useState } from 'react' -import { useNavigate } from 'react-router-dom' - -const backend_url = - import.meta.env.VITE_BACKEND_URL ?? 'http://192.168.100.40:3000' - -type Shortener = { - id: number - link: string - code: string - visitor_count: string -} - -export default function App() { - const [shorteners, setShorteners] = useState([]) - const [isLoading, setIsLoading] = useState(false) - - const getShorteners = async () => { - setIsLoading(true) - const response = await fetch(backend_url + '/link', { - method: 'GET', - }) - - const data = (await response.json()).shorteners as Shortener[] - - setShorteners(data) - setIsLoading(false) - } - - useEffect(() => { - if (!shorteners.length) { - getShorteners() - } - }, []) - - return ( - <> -
-
- -
-
- - - ) -} - -export const Navbar = () => { - const navigate = useNavigate() - return ( -
-
-
navigate('/')}> - Shortener -
-
- - - - - - - -
-
-
- ) -} -const CreateShortener = ({ - getShorteners, -}: { - getShorteners: () => Promise -}) => { - const [isOpen, setIsOpen] = useState(false) - const [link, setLink] = useState('') - const [error, setError] = useState('') - const addShortener = async () => { - setError('') - await fetch(backend_url + '/link', { - headers: { - 'Content-Type': 'application/json', - }, - method: 'POST', - body: JSON.stringify({ - link: link.startsWith('https://') ? link : 'https://' + link, - }), - }).then((response) => { - if (response.status === 400) { - setError('Invalid Url') - return - } - getShorteners() - setLink('') - setIsOpen(false) - }) - } - return ( - - - - - - - Add Shortener - - Create a new shortener for your link. - - -
-
- - setLink(e.target.value)} - /> -
-
- -
{error}
-
-
- - - -
-
- ) -} - -const ShortenerTable = ({ - shorteners, - isLoading, - getShorteners, -}: { - shorteners: Shortener[] - isLoading: boolean - getShorteners: () => Promise -}) => { - const navigate = useNavigate() - - return ( - - - -
-
Shorteners
- {isLoading && ( - - )} -
- -
-
- - - {!shorteners.length && ( - No Shorteners - )} - - - Link - Shortener - - Visitors - - - - - - {shorteners.length ? ( - shorteners.map((shortener) => ( - - {shortener.link} - -
- {shortener.code} -
- -
- - {shortener.visitor_count} - - - - - - - - - Actions - - - { - navigate( - '/dashboard/' + - shortener.code - ) - }}> - - View Details - - - {' '} - -
- )) - ) : ( - - )} -
-
-
-
- ) -} - -const ShortenerCopyButton = ({ code }: { code: string }) => { - const { toast } = useToast() - const copyLinkToClipboard = async (code: string) => { - await navigator.clipboard.writeText(backend_url + '/' + code) - toast({ - title: 'Link Copied', - description: `Copied ${backend_url + '/' + code} To Clipboard`, - }) - } - const [isCopy, setIsCopy] = useState(false) - const onClick = () => { - setIsCopy(true) - copyLinkToClipboard(code) - setTimeout(() => setIsCopy(false), 2000) - } - return ( - - ) -} diff --git a/react-frontend/src/components/mode-toggle.tsx b/react-frontend/src/components/mode-toggle.tsx deleted file mode 100644 index 155e199..0000000 --- a/react-frontend/src/components/mode-toggle.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { Moon, Sun } from 'lucide-react' - -import { Button } from '@/components/ui/button' -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger, -} from '@/components/ui/dropdown-menu' -import { useTheme } from '@/components/theme-provider' - -export function ModeToggle() { - const { setTheme } = useTheme() - - return ( - - - - - - setTheme('light')}> - Light - - setTheme('dark')}> - Dark - - setTheme('system')}> - System - - - - ) -} diff --git a/react-frontend/src/components/theme-provider.tsx b/react-frontend/src/components/theme-provider.tsx deleted file mode 100644 index a3c765a..0000000 --- a/react-frontend/src/components/theme-provider.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import { createContext, useContext, useEffect, useState } from 'react' - -type Theme = 'dark' | 'light' | 'system' - -type ThemeProviderProps = { - children: React.ReactNode - defaultTheme?: Theme - storageKey?: string -} - -type ThemeProviderState = { - theme: Theme - setTheme: (theme: Theme) => void -} - -const initialState: ThemeProviderState = { - theme: 'system', - setTheme: () => null, -} - -const ThemeProviderContext = createContext(initialState) - -export function ThemeProvider({ - children, - defaultTheme = 'system', - storageKey = 'vite-ui-theme', - ...props -}: ThemeProviderProps) { - const [theme, setTheme] = useState( - () => (localStorage.getItem(storageKey) as Theme) || defaultTheme - ) - - useEffect(() => { - const root = window.document.documentElement - - root.classList.remove('light', 'dark') - - if (theme === 'system') { - const systemTheme = window.matchMedia( - '(prefers-color-scheme: dark)' - ).matches - ? 'dark' - : 'light' - - root.classList.add(systemTheme) - return - } - - root.classList.add(theme) - }, [theme]) - - const value = { - theme, - setTheme: (theme: Theme) => { - localStorage.setItem(storageKey, theme) - setTheme(theme) - }, - } - - return ( - - {children} - - ) -} - -export const useTheme = () => { - const context = useContext(ThemeProviderContext) - - if (context === undefined) - throw new Error('useTheme must be used within a ThemeProvider') - - return context -} diff --git a/react-frontend/src/components/ui/alert-dialog.tsx b/react-frontend/src/components/ui/alert-dialog.tsx deleted file mode 100644 index c3dceec..0000000 --- a/react-frontend/src/components/ui/alert-dialog.tsx +++ /dev/null @@ -1,143 +0,0 @@ -import * as React from "react" -import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog" - -import { cn } from "@/lib/utils" -import { buttonVariants } from "@/components/ui/button" - -const AlertDialog = AlertDialogPrimitive.Root - -const AlertDialogTrigger = AlertDialogPrimitive.Trigger - -const AlertDialogPortal = ({ - className, - ...props -}: AlertDialogPrimitive.AlertDialogPortalProps) => ( - -) -AlertDialogPortal.displayName = AlertDialogPrimitive.Portal.displayName - -const AlertDialogOverlay = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, children, ...props }, ref) => ( - -)) -AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName - -const AlertDialogContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - - - - -)) -AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName - -const AlertDialogHeader = ({ - className, - ...props -}: React.HTMLAttributes) => ( -
-) -AlertDialogHeader.displayName = "AlertDialogHeader" - -const AlertDialogFooter = ({ - className, - ...props -}: React.HTMLAttributes) => ( -
-) -AlertDialogFooter.displayName = "AlertDialogFooter" - -const AlertDialogTitle = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName - -const AlertDialogDescription = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -AlertDialogDescription.displayName = - AlertDialogPrimitive.Description.displayName - -const AlertDialogAction = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName - -const AlertDialogCancel = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName - -export { - AlertDialog, - AlertDialogTrigger, - AlertDialogContent, - AlertDialogHeader, - AlertDialogFooter, - AlertDialogTitle, - AlertDialogDescription, - AlertDialogAction, - AlertDialogCancel, -} diff --git a/react-frontend/src/components/ui/avatar.tsx b/react-frontend/src/components/ui/avatar.tsx deleted file mode 100644 index 991f56e..0000000 --- a/react-frontend/src/components/ui/avatar.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import * as React from "react" -import * as AvatarPrimitive from "@radix-ui/react-avatar" - -import { cn } from "@/lib/utils" - -const Avatar = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -Avatar.displayName = AvatarPrimitive.Root.displayName - -const AvatarImage = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -AvatarImage.displayName = AvatarPrimitive.Image.displayName - -const AvatarFallback = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName - -export { Avatar, AvatarImage, AvatarFallback } diff --git a/react-frontend/src/components/ui/button.tsx b/react-frontend/src/components/ui/button.tsx deleted file mode 100644 index ac8e0c9..0000000 --- a/react-frontend/src/components/ui/button.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import * as React from "react" -import { Slot } from "@radix-ui/react-slot" -import { cva, type VariantProps } from "class-variance-authority" - -import { cn } from "@/lib/utils" - -const buttonVariants = cva( - "inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", - { - variants: { - variant: { - default: "bg-primary text-primary-foreground hover:bg-primary/90", - destructive: - "bg-destructive text-destructive-foreground hover:bg-destructive/90", - outline: - "border border-input bg-background hover:bg-accent hover:text-accent-foreground", - secondary: - "bg-secondary text-secondary-foreground hover:bg-secondary/80", - ghost: "hover:bg-accent hover:text-accent-foreground", - link: "text-primary underline-offset-4 hover:underline", - }, - size: { - default: "h-10 px-4 py-2", - sm: "h-9 rounded-md px-3", - lg: "h-11 rounded-md px-8", - icon: "h-10 w-10", - }, - }, - defaultVariants: { - variant: "default", - size: "default", - }, - } -) - -export interface ButtonProps - extends React.ButtonHTMLAttributes, - VariantProps { - asChild?: boolean -} - -const Button = React.forwardRef( - ({ className, variant, size, asChild = false, ...props }, ref) => { - const Comp = asChild ? Slot : "button" - return ( - - ) - } -) -Button.displayName = "Button" - -export { Button, buttonVariants } diff --git a/react-frontend/src/components/ui/card.tsx b/react-frontend/src/components/ui/card.tsx deleted file mode 100644 index afa13ec..0000000 --- a/react-frontend/src/components/ui/card.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import * as React from "react" - -import { cn } from "@/lib/utils" - -const Card = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
-)) -Card.displayName = "Card" - -const CardHeader = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
-)) -CardHeader.displayName = "CardHeader" - -const CardTitle = React.forwardRef< - HTMLParagraphElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -

-)) -CardTitle.displayName = "CardTitle" - -const CardDescription = React.forwardRef< - HTMLParagraphElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -

-)) -CardDescription.displayName = "CardDescription" - -const CardContent = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -

-)) -CardContent.displayName = "CardContent" - -const CardFooter = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
-)) -CardFooter.displayName = "CardFooter" - -export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } diff --git a/react-frontend/src/components/ui/dialog.tsx b/react-frontend/src/components/ui/dialog.tsx deleted file mode 100644 index f50d5d0..0000000 --- a/react-frontend/src/components/ui/dialog.tsx +++ /dev/null @@ -1,121 +0,0 @@ -import * as React from "react" -import * as DialogPrimitive from "@radix-ui/react-dialog" -import { X } from "lucide-react" - -import { cn } from "@/lib/utils" - -const Dialog = DialogPrimitive.Root - -const DialogTrigger = DialogPrimitive.Trigger - -const DialogPortal = ({ - className, - ...props -}: DialogPrimitive.DialogPortalProps) => ( - -) -DialogPortal.displayName = DialogPrimitive.Portal.displayName - -const DialogOverlay = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -DialogOverlay.displayName = DialogPrimitive.Overlay.displayName - -const DialogContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, children, ...props }, ref) => ( - - - - {children} - - - Close - - - -)) -DialogContent.displayName = DialogPrimitive.Content.displayName - -const DialogHeader = ({ - className, - ...props -}: React.HTMLAttributes) => ( -
-) -DialogHeader.displayName = "DialogHeader" - -const DialogFooter = ({ - className, - ...props -}: React.HTMLAttributes) => ( -
-) -DialogFooter.displayName = "DialogFooter" - -const DialogTitle = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -DialogTitle.displayName = DialogPrimitive.Title.displayName - -const DialogDescription = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -DialogDescription.displayName = DialogPrimitive.Description.displayName - -export { - Dialog, - DialogTrigger, - DialogContent, - DialogHeader, - DialogFooter, - DialogTitle, - DialogDescription, -} diff --git a/react-frontend/src/components/ui/dropdown-menu.tsx b/react-frontend/src/components/ui/dropdown-menu.tsx deleted file mode 100644 index 769ff7a..0000000 --- a/react-frontend/src/components/ui/dropdown-menu.tsx +++ /dev/null @@ -1,198 +0,0 @@ -import * as React from "react" -import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu" -import { Check, ChevronRight, Circle } from "lucide-react" - -import { cn } from "@/lib/utils" - -const DropdownMenu = DropdownMenuPrimitive.Root - -const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger - -const DropdownMenuGroup = DropdownMenuPrimitive.Group - -const DropdownMenuPortal = DropdownMenuPrimitive.Portal - -const DropdownMenuSub = DropdownMenuPrimitive.Sub - -const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup - -const DropdownMenuSubTrigger = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef & { - inset?: boolean - } ->(({ className, inset, children, ...props }, ref) => ( - - {children} - - -)) -DropdownMenuSubTrigger.displayName = - DropdownMenuPrimitive.SubTrigger.displayName - -const DropdownMenuSubContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -DropdownMenuSubContent.displayName = - DropdownMenuPrimitive.SubContent.displayName - -const DropdownMenuContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, sideOffset = 4, ...props }, ref) => ( - - - -)) -DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName - -const DropdownMenuItem = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef & { - inset?: boolean - } ->(({ className, inset, ...props }, ref) => ( - -)) -DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName - -const DropdownMenuCheckboxItem = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, children, checked, ...props }, ref) => ( - - - - - - - {children} - -)) -DropdownMenuCheckboxItem.displayName = - DropdownMenuPrimitive.CheckboxItem.displayName - -const DropdownMenuRadioItem = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, children, ...props }, ref) => ( - - - - - - - {children} - -)) -DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName - -const DropdownMenuLabel = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef & { - inset?: boolean - } ->(({ className, inset, ...props }, ref) => ( - -)) -DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName - -const DropdownMenuSeparator = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName - -const DropdownMenuShortcut = ({ - className, - ...props -}: React.HTMLAttributes) => { - return ( - - ) -} -DropdownMenuShortcut.displayName = "DropdownMenuShortcut" - -export { - DropdownMenu, - DropdownMenuTrigger, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuCheckboxItem, - DropdownMenuRadioItem, - DropdownMenuLabel, - DropdownMenuSeparator, - DropdownMenuShortcut, - DropdownMenuGroup, - DropdownMenuPortal, - DropdownMenuSub, - DropdownMenuSubContent, - DropdownMenuSubTrigger, - DropdownMenuRadioGroup, -} diff --git a/react-frontend/src/components/ui/input.tsx b/react-frontend/src/components/ui/input.tsx deleted file mode 100644 index 677d05f..0000000 --- a/react-frontend/src/components/ui/input.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import * as React from "react" - -import { cn } from "@/lib/utils" - -export interface InputProps - extends React.InputHTMLAttributes {} - -const Input = React.forwardRef( - ({ className, type, ...props }, ref) => { - return ( - - ) - } -) -Input.displayName = "Input" - -export { Input } diff --git a/react-frontend/src/components/ui/label.tsx b/react-frontend/src/components/ui/label.tsx deleted file mode 100644 index 683faa7..0000000 --- a/react-frontend/src/components/ui/label.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import * as React from "react" -import * as LabelPrimitive from "@radix-ui/react-label" -import { cva, type VariantProps } from "class-variance-authority" - -import { cn } from "@/lib/utils" - -const labelVariants = cva( - "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" -) - -const Label = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef & - VariantProps ->(({ className, ...props }, ref) => ( - -)) -Label.displayName = LabelPrimitive.Root.displayName - -export { Label } diff --git a/react-frontend/src/components/ui/table.tsx b/react-frontend/src/components/ui/table.tsx deleted file mode 100644 index bb3a87f..0000000 --- a/react-frontend/src/components/ui/table.tsx +++ /dev/null @@ -1,114 +0,0 @@ -import * as React from "react" - -import { cn } from "@/lib/utils" - -const Table = React.forwardRef< - HTMLTableElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
- - -)) -Table.displayName = "Table" - -const TableHeader = React.forwardRef< - HTMLTableSectionElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( - -)) -TableHeader.displayName = "TableHeader" - -const TableBody = React.forwardRef< - HTMLTableSectionElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( - -)) -TableBody.displayName = "TableBody" - -const TableFooter = React.forwardRef< - HTMLTableSectionElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( - -)) -TableFooter.displayName = "TableFooter" - -const TableRow = React.forwardRef< - HTMLTableRowElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( - -)) -TableRow.displayName = "TableRow" - -const TableHead = React.forwardRef< - HTMLTableCellElement, - React.ThHTMLAttributes ->(({ className, ...props }, ref) => ( -
-)) -TableHead.displayName = "TableHead" - -const TableCell = React.forwardRef< - HTMLTableCellElement, - React.TdHTMLAttributes ->(({ className, ...props }, ref) => ( - -)) -TableCell.displayName = "TableCell" - -const TableCaption = React.forwardRef< - HTMLTableCaptionElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
-)) -TableCaption.displayName = "TableCaption" - -export { - Table, - TableHeader, - TableBody, - TableFooter, - TableHead, - TableRow, - TableCell, - TableCaption, -} diff --git a/react-frontend/src/components/ui/toast.tsx b/react-frontend/src/components/ui/toast.tsx deleted file mode 100644 index a822477..0000000 --- a/react-frontend/src/components/ui/toast.tsx +++ /dev/null @@ -1,127 +0,0 @@ -import * as React from "react" -import * as ToastPrimitives from "@radix-ui/react-toast" -import { cva, type VariantProps } from "class-variance-authority" -import { X } from "lucide-react" - -import { cn } from "@/lib/utils" - -const ToastProvider = ToastPrimitives.Provider - -const ToastViewport = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -ToastViewport.displayName = ToastPrimitives.Viewport.displayName - -const toastVariants = cva( - "group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full", - { - variants: { - variant: { - default: "border bg-background text-foreground", - destructive: - "destructive group border-destructive bg-destructive text-destructive-foreground", - }, - }, - defaultVariants: { - variant: "default", - }, - } -) - -const Toast = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef & - VariantProps ->(({ className, variant, ...props }, ref) => { - return ( - - ) -}) -Toast.displayName = ToastPrimitives.Root.displayName - -const ToastAction = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -ToastAction.displayName = ToastPrimitives.Action.displayName - -const ToastClose = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - - - -)) -ToastClose.displayName = ToastPrimitives.Close.displayName - -const ToastTitle = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -ToastTitle.displayName = ToastPrimitives.Title.displayName - -const ToastDescription = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -ToastDescription.displayName = ToastPrimitives.Description.displayName - -type ToastProps = React.ComponentPropsWithoutRef - -type ToastActionElement = React.ReactElement - -export { - type ToastProps, - type ToastActionElement, - ToastProvider, - ToastViewport, - Toast, - ToastTitle, - ToastDescription, - ToastClose, - ToastAction, -} diff --git a/react-frontend/src/components/ui/toaster.tsx b/react-frontend/src/components/ui/toaster.tsx deleted file mode 100644 index a2209ba..0000000 --- a/react-frontend/src/components/ui/toaster.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { - Toast, - ToastClose, - ToastDescription, - ToastProvider, - ToastTitle, - ToastViewport, -} from "@/components/ui/toast" -import { useToast } from "@/components/ui/use-toast" - -export function Toaster() { - const { toasts } = useToast() - - return ( - - {toasts.map(function ({ id, title, description, action, ...props }) { - return ( - -
- {title && {title}} - {description && ( - {description} - )} -
- {action} - -
- ) - })} - -
- ) -} diff --git a/react-frontend/src/components/ui/use-toast.ts b/react-frontend/src/components/ui/use-toast.ts deleted file mode 100644 index 90d8959..0000000 --- a/react-frontend/src/components/ui/use-toast.ts +++ /dev/null @@ -1,192 +0,0 @@ -// Inspired by react-hot-toast library -import * as React from "react" - -import type { - ToastActionElement, - ToastProps, -} from "@/components/ui/toast" - -const TOAST_LIMIT = 1 -const TOAST_REMOVE_DELAY = 1000000 - -type ToasterToast = ToastProps & { - id: string - title?: React.ReactNode - description?: React.ReactNode - action?: ToastActionElement -} - -const actionTypes = { - ADD_TOAST: "ADD_TOAST", - UPDATE_TOAST: "UPDATE_TOAST", - DISMISS_TOAST: "DISMISS_TOAST", - REMOVE_TOAST: "REMOVE_TOAST", -} as const - -let count = 0 - -function genId() { - count = (count + 1) % Number.MAX_VALUE - return count.toString() -} - -type ActionType = typeof actionTypes - -type Action = - | { - type: ActionType["ADD_TOAST"] - toast: ToasterToast - } - | { - type: ActionType["UPDATE_TOAST"] - toast: Partial - } - | { - type: ActionType["DISMISS_TOAST"] - toastId?: ToasterToast["id"] - } - | { - type: ActionType["REMOVE_TOAST"] - toastId?: ToasterToast["id"] - } - -interface State { - toasts: ToasterToast[] -} - -const toastTimeouts = new Map>() - -const addToRemoveQueue = (toastId: string) => { - if (toastTimeouts.has(toastId)) { - return - } - - const timeout = setTimeout(() => { - toastTimeouts.delete(toastId) - dispatch({ - type: "REMOVE_TOAST", - toastId: toastId, - }) - }, TOAST_REMOVE_DELAY) - - toastTimeouts.set(toastId, timeout) -} - -export const reducer = (state: State, action: Action): State => { - switch (action.type) { - case "ADD_TOAST": - return { - ...state, - toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT), - } - - case "UPDATE_TOAST": - return { - ...state, - toasts: state.toasts.map((t) => - t.id === action.toast.id ? { ...t, ...action.toast } : t - ), - } - - case "DISMISS_TOAST": { - const { toastId } = action - - // ! Side effects ! - This could be extracted into a dismissToast() action, - // but I'll keep it here for simplicity - if (toastId) { - addToRemoveQueue(toastId) - } else { - state.toasts.forEach((toast) => { - addToRemoveQueue(toast.id) - }) - } - - return { - ...state, - toasts: state.toasts.map((t) => - t.id === toastId || toastId === undefined - ? { - ...t, - open: false, - } - : t - ), - } - } - case "REMOVE_TOAST": - if (action.toastId === undefined) { - return { - ...state, - toasts: [], - } - } - return { - ...state, - toasts: state.toasts.filter((t) => t.id !== action.toastId), - } - } -} - -const listeners: Array<(state: State) => void> = [] - -let memoryState: State = { toasts: [] } - -function dispatch(action: Action) { - memoryState = reducer(memoryState, action) - listeners.forEach((listener) => { - listener(memoryState) - }) -} - -type Toast = Omit - -function toast({ ...props }: Toast) { - const id = genId() - - const update = (props: ToasterToast) => - dispatch({ - type: "UPDATE_TOAST", - toast: { ...props, id }, - }) - const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id }) - - dispatch({ - type: "ADD_TOAST", - toast: { - ...props, - id, - open: true, - onOpenChange: (open) => { - if (!open) dismiss() - }, - }, - }) - - return { - id: id, - dismiss, - update, - } -} - -function useToast() { - const [state, setState] = React.useState(memoryState) - - React.useEffect(() => { - listeners.push(setState) - return () => { - const index = listeners.indexOf(setState) - if (index > -1) { - listeners.splice(index, 1) - } - } - }, [state]) - - return { - ...state, - toast, - dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }), - } -} - -export { useToast, toast } diff --git a/react-frontend/src/index.css b/react-frontend/src/index.css deleted file mode 100644 index 0b46ea1..0000000 --- a/react-frontend/src/index.css +++ /dev/null @@ -1,76 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -@layer base { - :root { - --background: 0 0% 100%; - --foreground: 240 10% 3.9%; - - --card: 0 0% 100%; - --card-foreground: 240 10% 3.9%; - - --popover: 0 0% 100%; - --popover-foreground: 240 10% 3.9%; - - --primary: 240 5.9% 10%; - --primary-foreground: 0 0% 98%; - - --secondary: 240 4.8% 95.9%; - --secondary-foreground: 240 5.9% 10%; - - --muted: 240 4.8% 95.9%; - --muted-foreground: 240 3.8% 46.1%; - - --accent: 240 4.8% 95.9%; - --accent-foreground: 240 5.9% 10%; - - --destructive: 0 84.2% 60.2%; - --destructive-foreground: 0 0% 98%; - - --border: 240 5.9% 90%; - --input: 240 5.9% 90%; - --ring: 240 10% 3.9%; - - --radius: 0.5rem; - } - - .dark { - --background: 240 10% 3.9%; - --foreground: 0 0% 98%; - - --card: 240 10% 3.9%; - --card-foreground: 0 0% 98%; - - --popover: 240 10% 3.9%; - --popover-foreground: 0 0% 98%; - - --primary: 0 0% 98%; - --primary-foreground: 240 5.9% 10%; - - --secondary: 240 3.7% 15.9%; - --secondary-foreground: 0 0% 98%; - - --muted: 240 3.7% 15.9%; - --muted-foreground: 240 5% 64.9%; - - --accent: 240 3.7% 15.9%; - --accent-foreground: 0 0% 98%; - - --destructive: 0 62.8% 30.6%; - --destructive-foreground: 0 0% 98%; - - --border: 240 3.7% 15.9%; - --input: 240 3.7% 15.9%; - --ring: 240 4.9% 83.9%; - } -} - -@layer base { - * { - @apply border-border; - } - body { - @apply bg-background text-foreground; - } -} \ No newline at end of file diff --git a/react-frontend/src/lib/utils.ts b/react-frontend/src/lib/utils.ts deleted file mode 100644 index ec79801..0000000 --- a/react-frontend/src/lib/utils.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { type ClassValue, clsx } from "clsx" -import { twMerge } from "tailwind-merge" - -export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)) -} diff --git a/react-frontend/src/main.tsx b/react-frontend/src/main.tsx deleted file mode 100644 index 2226c88..0000000 --- a/react-frontend/src/main.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import React from 'react' -import ReactDOM from 'react-dom/client' -import App from './App.tsx' -import './index.css' -import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom' -import Dashboard from './pages/dashboard.tsx' -import { Layout } from './pages/Layout.tsx' - -ReactDOM.createRoot(document.getElementById('root')!).render( - - - - }> - }> - - } - /> - } - /> - - } - /> - - - - -) diff --git a/react-frontend/src/pages/Layout.tsx b/react-frontend/src/pages/Layout.tsx deleted file mode 100644 index 327d63a..0000000 --- a/react-frontend/src/pages/Layout.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { ThemeProvider } from '@/components/theme-provider' -import { Navbar } from '@/App.tsx' -import { Outlet } from 'react-router-dom' - -export function Layout() { - return ( - - - - - ) -} diff --git a/react-frontend/src/pages/dashboard.tsx b/react-frontend/src/pages/dashboard.tsx deleted file mode 100644 index 23be6c8..0000000 --- a/react-frontend/src/pages/dashboard.tsx +++ /dev/null @@ -1,270 +0,0 @@ -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' -import { - Table, - TableBody, - TableCell, - TableHead, - TableHeader, - TableRow, -} from '@/components/ui/table' - -import { useParams } from 'react-router-dom' -import { useEffect, useState } from 'react' - -import { Bar, BarChart, ResponsiveContainer, XAxis, YAxis } from 'recharts' -import { ArrowRight, Copy } from 'lucide-react' -import { useToast } from '@/components/ui/use-toast' -import { Toaster } from '@/components/ui/toaster' - -type Shortener = { - id: number - link: string - code: string - visitors: { - visited_at: Date - country: string - }[] -} - -const months = [ - 'Jan', - 'Feb', - 'Mar', - 'Apr', - 'May', - 'Jun', - 'Jul', - 'Aug', - 'Sep', - 'Oct', - 'Nov', - 'Dec', -] as const - -type VisitorData = { name: string; total: number } -type CountryData = { - country_code: string - country: string - visitor_count: string -} -let visitor_data: VisitorData[] = [] -months.forEach((months) => { - visitor_data.push({ name: months, total: 0 }) -}) - -const backend_url = - import.meta.env.VITE_BACKEND_URL ?? 'http://192.168.100.40:3000' - -export default function Dashboard() { - const [shorteners, setShorteners] = useState([]) - const [isLoading, setIsLoading] = useState(false) - const { shortenerId } = useParams() - const [countryVisitor, setCountryVisitor] = useState([]) - - const [visitorData, setVisitorData] = useState(visitor_data) - - const getShorteners = async () => { - setIsLoading(true) - const response = await fetch(backend_url + '/link/' + shortenerId, { - method: 'GET', - }) - const data = await response.json() - const shortenersData = data.shorteners as Shortener[] - const countryData = data.visitors as CountryData[] - - setShorteners(shortenersData) - setCountryVisitor(countryData) - calculateShortenerData(shortenersData[0]) - setIsLoading(false) - } - - const calculateShortenerData = (shortener: Shortener) => { - const visitors = shortener.visitors - let data: VisitorData[] = [] - months.forEach((months) => { - data.push({ name: months, total: 0 }) - }) - let visitor_data_copy = data - - visitors.forEach((visitor) => { - const month = new Date(visitor.visited_at).getMonth() - visitor_data_copy[month] = { - ...visitor_data_copy[month], - total: visitor_data_copy[month].total + 1, - } - setVisitorData(visitor_data_copy) - }) - console.log(visitorData) - } - - const { toast } = useToast() - const copyLinkToClipboard = async (code: string) => { - await navigator.clipboard.writeText(backend_url + '/' + code) - toast({ - title: 'Link Copied', - description: `Copied ${backend_url + '/' + code} To Clipboard`, - }) - } - - useEffect(() => { - if (!shorteners.length) { - getShorteners() - } - }, []) - - const thisMonth = new Date().getMonth() - - const getVisitorGrowth = (visitorData: VisitorData[]) => { - const growth = visitorData[thisMonth]?.total - ? visitorData[thisMonth].total - - visitorData[thisMonth - 1]?.total ?? 0 - : 0 - - if (growth < 0) { - return `- ${Math.abs(growth)}` - } - - return `+ ${growth}` - } - - return ( - <> -
-
-
-
- copyLinkToClipboard(shorteners[0].code) - }> -
- {backend_url + '/' + shortenerId} - -
-
- - {shorteners[0]?.link} -
-
-
- - - - This Month's Visitors - - - - - - -
- {visitorData[thisMonth]?.total ?? 0} -
-

- {`${getVisitorGrowth( - visitorData - )} since last month`} -

-
-
-
-
- - - Visitors - - - - - - - - - Top Countries By Vistors - - - - - - - - Country - - Visitor(s) - - - - - {countryVisitor.map((country) => { - return ( - - - - - -
- { - country.country - } -
-
- - { - country.visitor_count - } - -
- ) - })} -
-
-
-
-
-
-
-
- - - ) -} - -export function Overview({ visitorData }: { visitorData: VisitorData[] }) { - return ( - - - - - - - - ) -} diff --git a/react-frontend/src/vite-env.d.ts b/react-frontend/src/vite-env.d.ts deleted file mode 100644 index 11f02fe..0000000 --- a/react-frontend/src/vite-env.d.ts +++ /dev/null @@ -1 +0,0 @@ -/// diff --git a/react-frontend/tailwind.config.js b/react-frontend/tailwind.config.js deleted file mode 100644 index 0377ea1..0000000 --- a/react-frontend/tailwind.config.js +++ /dev/null @@ -1,76 +0,0 @@ -/** @type {import('tailwindcss').Config} */ -module.exports = { - darkMode: ["class"], - content: [ - './pages/**/*.{ts,tsx}', - './components/**/*.{ts,tsx}', - './app/**/*.{ts,tsx}', - './src/**/*.{ts,tsx}', - ], - theme: { - container: { - center: true, - padding: "2rem", - screens: { - "2xl": "1400px", - }, - }, - extend: { - colors: { - border: "hsl(var(--border))", - input: "hsl(var(--input))", - ring: "hsl(var(--ring))", - background: "hsl(var(--background))", - foreground: "hsl(var(--foreground))", - primary: { - DEFAULT: "hsl(var(--primary))", - foreground: "hsl(var(--primary-foreground))", - }, - secondary: { - DEFAULT: "hsl(var(--secondary))", - foreground: "hsl(var(--secondary-foreground))", - }, - destructive: { - DEFAULT: "hsl(var(--destructive))", - foreground: "hsl(var(--destructive-foreground))", - }, - muted: { - DEFAULT: "hsl(var(--muted))", - foreground: "hsl(var(--muted-foreground))", - }, - accent: { - DEFAULT: "hsl(var(--accent))", - foreground: "hsl(var(--accent-foreground))", - }, - popover: { - DEFAULT: "hsl(var(--popover))", - foreground: "hsl(var(--popover-foreground))", - }, - card: { - DEFAULT: "hsl(var(--card))", - foreground: "hsl(var(--card-foreground))", - }, - }, - borderRadius: { - lg: "var(--radius)", - md: "calc(var(--radius) - 2px)", - sm: "calc(var(--radius) - 4px)", - }, - keyframes: { - "accordion-down": { - from: { height: 0 }, - to: { height: "var(--radix-accordion-content-height)" }, - }, - "accordion-up": { - from: { height: "var(--radix-accordion-content-height)" }, - to: { height: 0 }, - }, - }, - animation: { - "accordion-down": "accordion-down 0.2s ease-out", - "accordion-up": "accordion-up 0.2s ease-out", - }, - }, - }, - plugins: [require("tailwindcss-animate")], -} \ No newline at end of file diff --git a/react-frontend/tsconfig.json b/react-frontend/tsconfig.json deleted file mode 100644 index 50a8e30..0000000 --- a/react-frontend/tsconfig.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2020", - "useDefineForClassFields": true, - "lib": ["ES2020", "DOM", "DOM.Iterable"], - "module": "ESNext", - "skipLibCheck": true, - - /* Bundler mode */ - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "resolveJsonModule": true, - "isolatedModules": true, - "noEmit": true, - "jsx": "react-jsx", - - /* Linting */ - "strict": true, - // "noUnusedLocals": true, - // "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true, - "baseUrl": ".", - "paths": { - "@/*": ["./src/*"] - } - }, - "include": ["src"], - "references": [{ "path": "./tsconfig.node.json" }] -} diff --git a/react-frontend/tsconfig.node.json b/react-frontend/tsconfig.node.json deleted file mode 100644 index 42872c5..0000000 --- a/react-frontend/tsconfig.node.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "compilerOptions": { - "composite": true, - "skipLibCheck": true, - "module": "ESNext", - "moduleResolution": "bundler", - "allowSyntheticDefaultImports": true - }, - "include": ["vite.config.ts"] -} diff --git a/react-frontend/vite.config.ts b/react-frontend/vite.config.ts deleted file mode 100644 index 2f1370b..0000000 --- a/react-frontend/vite.config.ts +++ /dev/null @@ -1,12 +0,0 @@ -import path from 'path' -import react from '@vitejs/plugin-react' -import { defineConfig } from 'vite' - -export default defineConfig({ - plugins: [react()], - resolve: { - alias: { - '@': path.resolve(__dirname, './src'), - }, - }, -}) diff --git a/vue-frontend/.env b/vue-frontend/.env deleted file mode 100644 index cf5fdfe..0000000 --- a/vue-frontend/.env +++ /dev/null @@ -1,2 +0,0 @@ -BASE_URL=https://5173.tzgyn.com -BACKEND_URL=https://3000.tzgyn.com diff --git a/vue-frontend/.gitignore b/vue-frontend/.gitignore deleted file mode 100644 index 38adffa..0000000 --- a/vue-frontend/.gitignore +++ /dev/null @@ -1,28 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* -lerna-debug.log* - -node_modules -.DS_Store -dist -dist-ssr -coverage -*.local - -/cypress/videos/ -/cypress/screenshots/ - -# Editor directories and files -.vscode/* -!.vscode/extensions.json -.idea -*.suo -*.ntvs* -*.njsproj -*.sln -*.sw? diff --git a/vue-frontend/.prettierrc.yaml b/vue-frontend/.prettierrc.yaml deleted file mode 100644 index 1439e2e..0000000 --- a/vue-frontend/.prettierrc.yaml +++ /dev/null @@ -1,16 +0,0 @@ ---- -printWidth: 80 -tabWidth: 4 -useTabs: true -semi: false -singleQuote: true -quoteProps: consistent -jsxSingleQuote: true -trailingComma: es5 -bracketSpacing: true -bracketSameLine: true -arrowParens: always -htmlWhitespaceSensitivity: strict -vueIndentScriptAndStyle: false -singleAttributePerLine: true -plugins: [prettier-plugin-tailwindcss] diff --git a/vue-frontend/.vscode/extensions.json b/vue-frontend/.vscode/extensions.json deleted file mode 100644 index c0a6e5a..0000000 --- a/vue-frontend/.vscode/extensions.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"] -} diff --git a/vue-frontend/Dockerfile b/vue-frontend/Dockerfile deleted file mode 100644 index 2978fce..0000000 --- a/vue-frontend/Dockerfile +++ /dev/null @@ -1,15 +0,0 @@ -FROM docker.io/oven/bun - -RUN mkdir /shortener-frontend -WORKDIR /shortener-frontend - -COPY ./package.json ./ -COPY ./bun.lockb ./ - -RUN bun install - -COPY . . - -RUN bun run build - -ENTRYPOINT [ "bun", "run", "preview", "--host" ] diff --git a/vue-frontend/README.md b/vue-frontend/README.md deleted file mode 100644 index e1cff9b..0000000 --- a/vue-frontend/README.md +++ /dev/null @@ -1,40 +0,0 @@ -# vue-frontend - -This template should help get you started developing with Vue 3 in Vite. - -## Recommended IDE Setup - -[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin). - -## Type Support for `.vue` Imports in TS - -TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin) to make the TypeScript language service aware of `.vue` types. - -If the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps: - -1. Disable the built-in TypeScript Extension - 1) Run `Extensions: Show Built-in Extensions` from VSCode's command palette - 2) Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)` -2. Reload the VSCode window by running `Developer: Reload Window` from the command palette. - -## Customize configuration - -See [Vite Configuration Reference](https://vitejs.dev/config/). - -## Project Setup - -```sh -npm install -``` - -### Compile and Hot-Reload for Development - -```sh -npm run dev -``` - -### Type-Check, Compile and Minify for Production - -```sh -npm run build -``` diff --git a/vue-frontend/bun.lockb b/vue-frontend/bun.lockb deleted file mode 100755 index 9635d62e91342695a3ab5e38869ab96482fb33cf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 191494 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 n%3E>LwF2a2G diff --git a/vue-frontend/index.html b/vue-frontend/index.html deleted file mode 100644 index 94d17d3..0000000 --- a/vue-frontend/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - Link Shortener - - -
- - - diff --git a/vue-frontend/package.json b/vue-frontend/package.json deleted file mode 100644 index 70a9ef6..0000000 --- a/vue-frontend/package.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "vue-frontend", - "version": "0.0.0", - "private": true, - "scripts": { - "dev": "vite", - "build": "run-p type-check \"build-only {@}\" --", - "preview": "vite preview", - "build-only": "vite build", - "type-check": "vue-tsc --noEmit -p tsconfig.app.json --composite false" - }, - "dependencies": { - "@vueuse/core": "^10.5.0", - "axios": "^1.6.0", - "class-variance-authority": "^0.7.0", - "clsx": "^2.0.0", - "lucide-vue-next": "^0.288.0", - "radix-vue": "^0.4.1", - "tailwind-merge": "^1.14.0", - "tailwindcss-animate": "^1.0.7", - "vue": "^3.3.4", - "vue-router": "^4.2.5" - }, - "devDependencies": { - "@tsconfig/node18": "^18.2.2", - "@types/node": "^18.18.5", - "@vitejs/plugin-vue": "^4.4.0", - "@vue/tsconfig": "^0.4.0", - "autoprefixer": "^10.4.16", - "bun-types": "^1.0.7", - "npm-run-all2": "^6.1.1", - "postcss": "^8.4.31", - "prettier": "^3.0.3", - "prettier-plugin-tailwindcss": "^0.5.6", - "tailwindcss": "^3.3.3", - "typescript": "~5.2.0", - "vite": "^4.4.11", - "vue-tsc": "^1.8.19" - } -} diff --git a/vue-frontend/postcss.config.js b/vue-frontend/postcss.config.js deleted file mode 100644 index 33ad091..0000000 --- a/vue-frontend/postcss.config.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - }, -} diff --git a/vue-frontend/public/favicon.ico b/vue-frontend/public/favicon.ico deleted file mode 100644 index df36fcfb72584e00488330b560ebcf34a41c64c2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4286 zcmds*O-Phc6o&64GDVCEQHxsW(p4>LW*W<827=Unuo8sGpRux(DN@jWP-e29Wl%wj zY84_aq9}^Am9-cWTD5GGEo#+5Fi2wX_P*bo+xO!)p*7B;iKlbFd(U~_d(U?#hLj56 zPhFkj-|A6~Qk#@g^#D^U0XT1cu=c-vu1+SElX9NR;kzAUV(q0|dl0|%h|dI$%VICy zJnu2^L*Te9JrJMGh%-P79CL0}dq92RGU6gI{v2~|)p}sG5x0U*z<8U;Ij*hB9z?ei z@g6Xq-pDoPl=MANPiR7%172VA%r)kevtV-_5H*QJKFmd;8yA$98zCxBZYXTNZ#QFk2(TX0;Y2dt&WitL#$96|gJY=3xX zpCoi|YNzgO3R`f@IiEeSmKrPSf#h#Qd<$%Ej^RIeeYfsxhPMOG`S`Pz8q``=511zm zAm)MX5AV^5xIWPyEu7u>qYs?pn$I4nL9J!=K=SGlKLXpE<5x+2cDTXq?brj?n6sp= zphe9;_JHf40^9~}9i08r{XM$7HB!`{Ys~TK0kx<}ZQng`UPvH*11|q7&l9?@FQz;8 zx!=3<4seY*%=OlbCbcae?5^V_}*K>Uo6ZWV8mTyE^B=DKy7-sdLYkR5Z?paTgK-zyIkKjIcpyO z{+uIt&YSa_$QnN_@t~L014dyK(fOOo+W*MIxbA6Ndgr=Y!f#Tokqv}n<7-9qfHkc3 z=>a|HWqcX8fzQCT=dqVbogRq!-S>H%yA{1w#2Pn;=e>JiEj7Hl;zdt-2f+j2%DeVD zsW0Ab)ZK@0cIW%W7z}H{&~yGhn~D;aiP4=;m-HCo`BEI+Kd6 z={Xwx{TKxD#iCLfl2vQGDitKtN>z|-AdCN|$jTFDg0m3O`WLD4_s#$S diff --git a/vue-frontend/src/App.vue b/vue-frontend/src/App.vue deleted file mode 100644 index e9f44f1..0000000 --- a/vue-frontend/src/App.vue +++ /dev/null @@ -1,11 +0,0 @@ - - - diff --git a/vue-frontend/src/assets/index.css b/vue-frontend/src/assets/index.css deleted file mode 100644 index 865834e..0000000 --- a/vue-frontend/src/assets/index.css +++ /dev/null @@ -1,80 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -@layer base { - :root { - --background: 0 0% 100%; - --foreground: 240 10% 3.9%; - - --card: 0 0% 100%; - --card-foreground: 240 10% 3.9%; - - --popover: 0 0% 100%; - --popover-foreground: 240 10% 3.9%; - - --primary: 240 5.9% 10%; - --primary-foreground: 0 0% 98%; - - --secondary: 240 4.8% 95.9%; - --secondary-foreground: 240 5.9% 10%; - - --muted: 240 4.8% 95.9%; - --muted-foreground: 240 3.8% 46.1%; - - --accent: 240 4.8% 95.9%; - --accent-foreground: 240 5.9% 10%; - - --destructive: 0 84.2% 60.2%; - --destructive-foreground: 0 0% 98%; - - --border: 240 5.9% 90%; - --input: 240 5.9% 90%; - --ring: 240 10% 3.9%; - - --radius: 0.5rem; - } - - .dark { - --background: 240 10% 3.9%; - --foreground: 0 0% 98%; - - --card: 240 10% 3.9%; - --card-foreground: 0 0% 98%; - - --popover: 240 10% 3.9%; - --popover-foreground: 0 0% 98%; - - --primary: 0 0% 98%; - --primary-foreground: 240 5.9% 10%; - - --secondary: 240 3.7% 15.9%; - --secondary-foreground: 0 0% 98%; - - --muted: 240 3.7% 15.9%; - --muted-foreground: 240 5% 64.9%; - - --accent: 240 3.7% 15.9%; - --accent-foreground: 0 0% 98%; - - --destructive: 0 62.8% 30.6%; - --destructive-foreground: 0 0% 98%; - - --border: 240 3.7% 15.9%; - --input: 240 3.7% 15.9%; - --ring: 240 4.9% 83.9%; - } -} - -@layer base { - * { - @apply border-border; - } - body { - @apply bg-background text-foreground; - } -} - -#app { - @apply h-screen w-screen; -} diff --git a/vue-frontend/src/assets/logo.svg b/vue-frontend/src/assets/logo.svg deleted file mode 100644 index 7565660..0000000 --- a/vue-frontend/src/assets/logo.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/vue-frontend/src/components/Navbar.vue b/vue-frontend/src/components/Navbar.vue deleted file mode 100644 index 7c6aa55..0000000 --- a/vue-frontend/src/components/Navbar.vue +++ /dev/null @@ -1,21 +0,0 @@ - - diff --git a/vue-frontend/src/components/ToggleThemeButton.vue b/vue-frontend/src/components/ToggleThemeButton.vue deleted file mode 100644 index c2054a8..0000000 --- a/vue-frontend/src/components/ToggleThemeButton.vue +++ /dev/null @@ -1,21 +0,0 @@ - - - diff --git a/vue-frontend/src/components/UserLoginForm.vue b/vue-frontend/src/components/UserLoginForm.vue deleted file mode 100644 index b7f9f7a..0000000 --- a/vue-frontend/src/components/UserLoginForm.vue +++ /dev/null @@ -1,94 +0,0 @@ - - - diff --git a/vue-frontend/src/components/UserSignUpForm.vue b/vue-frontend/src/components/UserSignUpForm.vue deleted file mode 100644 index 83bcc72..0000000 --- a/vue-frontend/src/components/UserSignUpForm.vue +++ /dev/null @@ -1,117 +0,0 @@ - - - diff --git a/vue-frontend/src/components/ui/button/Button.vue b/vue-frontend/src/components/ui/button/Button.vue deleted file mode 100644 index d721b1a..0000000 --- a/vue-frontend/src/components/ui/button/Button.vue +++ /dev/null @@ -1,23 +0,0 @@ - - - diff --git a/vue-frontend/src/components/ui/button/index.ts b/vue-frontend/src/components/ui/button/index.ts deleted file mode 100644 index 4211057..0000000 --- a/vue-frontend/src/components/ui/button/index.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { cva } from 'class-variance-authority' - -export { default as Button } from './Button.vue' - -export const buttonVariants = cva( - 'inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50', - { - variants: { - variant: { - default: - 'bg-primary-foreground text-primary hover:bg-primary-foreground/90', - destructive: - 'bg-destructive text-destructive-foreground hover:bg-destructive/90', - outline: - 'border border-input bg-background hover:bg-accent hover:text-accent-foreground', - secondary: - 'bg-secondary text-secondary-foreground hover:bg-secondary/80', - ghost: 'hover:bg-accent hover:text-accent-foreground', - link: 'text-primary underline-offset-4 hover:underline', - }, - size: { - default: 'h-10 px-4 py-2', - sm: 'h-9 rounded-md px-3', - lg: 'h-11 rounded-md px-8', - icon: 'h-10 w-10', - }, - }, - defaultVariants: { - variant: 'default', - size: 'default', - }, - } -) diff --git a/vue-frontend/src/components/ui/input/Input.vue b/vue-frontend/src/components/ui/input/Input.vue deleted file mode 100644 index 66ece7a..0000000 --- a/vue-frontend/src/components/ui/input/Input.vue +++ /dev/null @@ -1,22 +0,0 @@ - - - diff --git a/vue-frontend/src/components/ui/input/index.ts b/vue-frontend/src/components/ui/input/index.ts deleted file mode 100644 index a691dd6..0000000 --- a/vue-frontend/src/components/ui/input/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as Input } from './Input.vue' diff --git a/vue-frontend/src/components/ui/label/Label.vue b/vue-frontend/src/components/ui/label/Label.vue deleted file mode 100644 index e44d139..0000000 --- a/vue-frontend/src/components/ui/label/Label.vue +++ /dev/null @@ -1,20 +0,0 @@ - - - diff --git a/vue-frontend/src/components/ui/label/index.ts b/vue-frontend/src/components/ui/label/index.ts deleted file mode 100644 index 572c2f0..0000000 --- a/vue-frontend/src/components/ui/label/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as Label } from './Label.vue' diff --git a/vue-frontend/src/lib/auth.ts b/vue-frontend/src/lib/auth.ts deleted file mode 100644 index b6c2799..0000000 --- a/vue-frontend/src/lib/auth.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const isAuthenticated = async () => { - return false -} diff --git a/vue-frontend/src/lib/fetch.ts b/vue-frontend/src/lib/fetch.ts deleted file mode 100644 index 460792a..0000000 --- a/vue-frontend/src/lib/fetch.ts +++ /dev/null @@ -1,7 +0,0 @@ -import axios from 'axios' - -const backend_url = import.meta.env.BACKEND_URL || 'https://3000.tzgyn.com' - -export const customAxios = axios.create({ - baseURL: backend_url -}) diff --git a/vue-frontend/src/lib/utils.ts b/vue-frontend/src/lib/utils.ts deleted file mode 100644 index 24470b3..0000000 --- a/vue-frontend/src/lib/utils.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { type ClassValue, clsx } from 'clsx' -import { twMerge } from 'tailwind-merge' -import { camelize, getCurrentInstance, toHandlerKey } from 'vue' - -export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)) -} diff --git a/vue-frontend/src/main.ts b/vue-frontend/src/main.ts deleted file mode 100644 index 72d93d8..0000000 --- a/vue-frontend/src/main.ts +++ /dev/null @@ -1,11 +0,0 @@ -import "./assets/index.css"; - -import { createApp } from "vue"; -import App from "./App.vue"; -import router from "./router"; - -const app = createApp(App); - -app.use(router); - -app.mount("#app"); diff --git a/vue-frontend/src/router/index.ts b/vue-frontend/src/router/index.ts deleted file mode 100644 index 5093e2e..0000000 --- a/vue-frontend/src/router/index.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { createRouter, createWebHistory } from 'vue-router' -import { isAuthenticated } from '@/lib/auth' -import HomeView from '@/views/HomeView.vue' -import LoginPage from '@/views/LoginPage.vue' -import SignUpPage from '@/views/SignUpPage.vue' - -const router = createRouter({ - history: createWebHistory(import.meta.env.BASE_URL), - routes: [ - { - path: '/', - name: 'home', - component: HomeView, - }, - { - path: '/login', - name: 'login', - component: LoginPage, - }, - { - path: '/signup', - name: 'signup', - component: SignUpPage, - }, - { - path: '/:pathMatch(.*)*', - name: '404', - component: HomeView, - }, - ], -}) - -router.beforeEach(async (to) => { - const isLoggedIn = await isAuthenticated() - if (isLoggedIn && (to.name === 'login' || to.name === 'signup')) { - return { name: 'home' } - } - if (!isLoggedIn && to.name !== 'login' && to.name !== 'signup') { - return { name: 'login' } - } -}) - -export default router diff --git a/vue-frontend/src/views/HomeView.vue b/vue-frontend/src/views/HomeView.vue deleted file mode 100644 index 0a45c22..0000000 --- a/vue-frontend/src/views/HomeView.vue +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/vue-frontend/src/views/LoginPage.vue b/vue-frontend/src/views/LoginPage.vue deleted file mode 100644 index cd18c1e..0000000 --- a/vue-frontend/src/views/LoginPage.vue +++ /dev/null @@ -1,43 +0,0 @@ - - - diff --git a/vue-frontend/src/views/SignUpPage.vue b/vue-frontend/src/views/SignUpPage.vue deleted file mode 100644 index f759724..0000000 --- a/vue-frontend/src/views/SignUpPage.vue +++ /dev/null @@ -1,43 +0,0 @@ - - - diff --git a/vue-frontend/tailwind.config.js b/vue-frontend/tailwind.config.js deleted file mode 100644 index aedb250..0000000 --- a/vue-frontend/tailwind.config.js +++ /dev/null @@ -1,79 +0,0 @@ -/** @type {import('tailwindcss').Config} */ -module.exports = { - darkMode: ['class'], - - content: [ - './pages/**/*.{ts,tsx,vue}', - './components/**/*.{ts,tsx,vue}', - './app/**/*.{ts,tsx,vue}', - './src/**/*.{ts,tsx,vue}', - './index.html', - ], - - theme: { - container: { - center: true, - padding: '2rem', - screens: { - '2xl': '1400px', - }, - }, - extend: { - colors: { - border: 'hsl(var(--border))', - input: 'hsl(var(--input))', - ring: 'hsl(var(--ring))', - background: 'hsl(var(--background))', - foreground: 'hsl(var(--foreground))', - primary: { - DEFAULT: 'hsl(var(--primary))', - foreground: 'hsl(var(--primary-foreground))', - }, - secondary: { - DEFAULT: 'hsl(var(--secondary))', - foreground: 'hsl(var(--secondary-foreground))', - }, - destructive: { - DEFAULT: 'hsl(var(--destructive))', - foreground: 'hsl(var(--destructive-foreground))', - }, - muted: { - DEFAULT: 'hsl(var(--muted))', - foreground: 'hsl(var(--muted-foreground))', - }, - accent: { - DEFAULT: 'hsl(var(--accent))', - foreground: 'hsl(var(--accent-foreground))', - }, - popover: { - DEFAULT: 'hsl(var(--popover))', - foreground: 'hsl(var(--popover-foreground))', - }, - card: { - DEFAULT: 'hsl(var(--card))', - foreground: 'hsl(var(--card-foreground))', - }, - }, - borderRadius: { - lg: 'var(--radius)', - md: 'calc(var(--radius) - 2px)', - sm: 'calc(var(--radius) - 4px)', - }, - keyframes: { - 'accordion-down': { - from: { height: 0 }, - to: { height: 'var(--radix-accordion-content-height)' }, - }, - 'accordion-up': { - from: { height: 'var(--radix-accordion-content-height)' }, - to: { height: 0 }, - }, - }, - animation: { - 'accordion-down': 'accordion-down 0.2s ease-out', - 'accordion-up': 'accordion-up 0.2s ease-out', - }, - }, - }, - plugins: [require('tailwindcss-animate')], -} diff --git a/vue-frontend/tsconfig.app.json b/vue-frontend/tsconfig.app.json deleted file mode 100644 index 3e5b621..0000000 --- a/vue-frontend/tsconfig.app.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": "@vue/tsconfig/tsconfig.dom.json", - "include": ["env.d.ts", "src/**/*", "src/**/*.vue"], - "exclude": ["src/**/__tests__/*"], - "compilerOptions": { - "composite": true, - "baseUrl": ".", - "paths": { - "@/*": ["./src/*"] - } - } -} diff --git a/vue-frontend/tsconfig.json b/vue-frontend/tsconfig.json deleted file mode 100644 index 66b5e57..0000000 --- a/vue-frontend/tsconfig.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "files": [], - "references": [ - { - "path": "./tsconfig.node.json" - }, - { - "path": "./tsconfig.app.json" - } - ] -} diff --git a/vue-frontend/tsconfig.node.json b/vue-frontend/tsconfig.node.json deleted file mode 100644 index 518d711..0000000 --- a/vue-frontend/tsconfig.node.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "extends": "@tsconfig/node18/tsconfig.json", - "include": [ - "vite.config.*", - "vitest.config.*", - "cypress.config.*", - "nightwatch.conf.*", - "playwright.config.*" - ], - "compilerOptions": { - "composite": true, - "module": "ESNext", - "moduleResolution": "Bundler", - "types": ["bun-types"] - } -} diff --git a/vue-frontend/vite.config.ts b/vue-frontend/vite.config.ts deleted file mode 100644 index 5c45e1d..0000000 --- a/vue-frontend/vite.config.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { fileURLToPath, URL } from 'node:url' - -import { defineConfig } from 'vite' -import vue from '@vitejs/plugin-vue' - -// https://vitejs.dev/config/ -export default defineConfig({ - plugins: [ - vue(), - ], - resolve: { - alias: { - '@': fileURLToPath(new URL('./src', import.meta.url)) - } - } -})