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 f62cded..0000000 Binary files a/elysia/bun.lockb and /dev/null differ 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 95fa885..0000000 Binary files a/react-frontend/bun.lockb and /dev/null differ diff --git a/react-frontend/components.json b/react-frontend/components.json deleted file mode 100644 index 1ff8288..0000000 --- a/react-frontend/components.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "$schema": "https://ui.shadcn.com/schema.json", - "style": "default", - "rsc": false, - "tsx": true, - "tailwind": { - "config": "tailwind.config.js", - "css": "src/index.css", - "baseColor": "zinc", - "cssVariables": true - }, - "aliases": { - "components": "@/components", - "utils": "@/lib/utils" - } -} \ No newline at end of file diff --git a/react-frontend/index.html b/react-frontend/index.html deleted file mode 100644 index 9272151..0000000 --- a/react-frontend/index.html +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - 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 9635d62..0000000 Binary files a/vue-frontend/bun.lockb and /dev/null differ diff --git a/vue-frontend/components.json b/vue-frontend/components.json deleted file mode 100644 index 59524eb..0000000 --- a/vue-frontend/components.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "style": "default", - "typescript": true, - "tailwind": { - "config": "tailwind.config.js", - "css": "src/assets/index.css", - "baseColor": "slate", - "cssVariables": true - }, - "framework": "vite", - "aliases": { - "components": "@/components", - "utils": "@/lib/utils" - } -} \ No newline at end of file diff --git a/vue-frontend/docker-compose.yml b/vue-frontend/docker-compose.yml deleted file mode 100644 index bca8ac5..0000000 --- a/vue-frontend/docker-compose.yml +++ /dev/null @@ -1,15 +0,0 @@ ---- -# docker-compose.yml -version: '3.9' -services: - app: - image: oven/bun - container_name: linkshortener_vue_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 dev --host' - # expose the right ports - ports: [5173:5173] - # setup a host mounted volume to sync changes to the container - volumes: [./:/home/bun/app] diff --git a/vue-frontend/env.d.ts b/vue-frontend/env.d.ts deleted file mode 100644 index 11f02fe..0000000 --- a/vue-frontend/env.d.ts +++ /dev/null @@ -1 +0,0 @@ -/// 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 df36fcf..0000000 Binary files a/vue-frontend/public/favicon.ico and /dev/null differ 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)) - } - } -})