From 7555e2297fbf13788fc753a182b722c5a8acdc56 Mon Sep 17 00:00:00 2001 From: moolcoov Date: Thu, 27 Feb 2025 16:40:13 +0300 Subject: [PATCH] init: add shadcn and ofetch --- services/frontend/bun.lockb | Bin 88611 -> 94828 bytes services/frontend/components.json | 21 +++ services/frontend/package.json | 13 +- .../frontend/src/components/ui/button.tsx | 58 +++++++++ services/frontend/src/lib/utils.ts | 6 + services/frontend/src/styles/globals.css | 123 ++++++++++++++++++ services/frontend/tsconfig.app.json | 6 +- services/frontend/tsconfig.json | 7 +- services/frontend/vite.config.ts | 3 +- 9 files changed, 232 insertions(+), 5 deletions(-) create mode 100644 services/frontend/components.json create mode 100644 services/frontend/src/components/ui/button.tsx create mode 100644 services/frontend/src/lib/utils.ts diff --git a/services/frontend/bun.lockb b/services/frontend/bun.lockb index f7155a5bfbf16e4cb519d18155c56a0f4c83f657..2c7d5b00b327c35395db1522bfd5fda8b5a65f3e 100644 GIT binary patch delta 19230 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

GcfhncunMRN zQ~-Q2<$-Pl1h5I<_Zm9$>y#BY16*+{z}G*ctiMgw`gO|X+W~HG2kY_tGs6Wtb-GKZ ze*oo+DVM(jaK%c1FQ%OT8Vk67jdJ;JwN#@#Wv}Zp(;`;br)Mzb4)z0_e*oZ%Da!|0 zz{QjsdJEwE!vJ58Q_eranYfMs-0s@|=N|+30!>phjzi#r6P$~SsS9w51ze9));q1{ z{&OmmdmnFK?)E)^YrU`6VrnM$fh~kY5d*z>3Zzc}Ht8x0xE`n6Mzxx&(K_J&4%7nf z0A}Dvfb|{$d@x?$uB5pxg+5Q>gv=} z*JH|TeO+eCtd}l7PPu+VQKa1`jiJEzOQ5dElsAMJU4EQ$RxA>xaY(qKcu;rHPMrB9 zR1Lv@QGwg+tT)V**)BTe*W|{<17`5Wln3^&cC#Yh#`t3TU);{rcAjt>Ec&Ce$w5n=)ZA0Q(gRoyIIKpZ)g9PwzCv{JNjq)Aglc5c1D-d3TX3SJFOV(P6LM6 z1>QFdaZtA*?qnKj7be<2+(CCC`H!%RT9iG)LEDD9(^*Jn@=bRLH=2mFHl0FRhXO}B zggX@?^`LV|>rz;TL)4=Zq@HvMX?=>$bOXY65V15LX&hA}Z9|C@93q}dky_~sqzTkvqC+IoN~CS+7Sbf@p63wBRDrY|-9u`l zzWEN(p0*=RAyMEE9cU2Jj#P=X6PYJDL}yAz+J*KbO(oCC4$+mek#?h_NV}8o6o=?R z6Or2K6w;m)IMpF~Q6bXabPnlL6jtaEeW(O!U%G^}A4N}dh^J{j(*9J9bO0qj=Mc|O zDN+Z0fpj2kpWzULNEA6l8Vy1^m@1JDA#<@q45f6W!)QO!;pAE3puaumPLoRPBAt#x zS~1<7f@j)A22GraNP6C#EmZ+*?zDNXUF6d(NGBl; zm}eK0sA8Ujrp`hf&$o*y)OWsvLT9_vUPy%`UUbmMkg{I1i|42k(!4qD)Np}aJWuHh z9F*{aJDq@3M4m6fK1h>ZvWpTr3Tee$*tgIwX3@ljux}oEm|+)lsP7Ee2k8RJ=2GAy z*f$^6F0zaHbPm$M7h&yUyI4Rai(%gaSPN+(MK6JUkd`g6i^Wt8Y0OKow$v_4sk9XK zErhj@h&n8VeULUUwToqR3)0j@u$JsXp$dY1i!qimyI4tm%U~a*y^vOsSO)u+z}jVY zv4$!kC6vP2<#w@-(wD+hP~*Q#GV98(`m7yEsRs zTOHy&eS!2t>afirKBARKFVHQd7pePphxij!ApMx`A-zO>Uv`Kp+K%+kBzC~6EwE~b zU0kM0NC{hE)lR#(O6fZt;xDv+=TnriXDWSA*Trpec0m>vdZ~>}9+iHaMfF~BHC@5u zSKZy7mG28R>zh|y8;!vaCl9=q;|DY2oXpGayFDsXYnZnBR0X^iFI3W$RhFPu1;>xASgjbs%UxXNS`dx|%*tK!?aWEURPa z`If8cP$%rds5@G%Yy20wU01E{F|qs2Bh}$<0aw`~ysNlM`@Zg8R0WndGvjI1klB#w z0AGIwScf04jb}anxy^a}BfkkVxGn?C^3(4>0KTpOoX0;*?*gpPBjCJ`ud2Vm@#EoB zdg11%BK|$dKSp^hg}^l6Ie^D81K_@kff8UQFbkLs%mH2i<^uD8`M`_70^lWJA+QKo z3@iakfu#TeWxz6EIXAC>SplpB@LVqy0FwY8fPaqp0gZtM0RPTx1n`ek{^e{15`aXY zEsz8x1ML7C&>lzuIsojvkAMrnpMbN#88+_(n0J7az!Bgmzz?@?0*8PDz&_v&U^lP_ z;Fn9=ftLaPO|S`A1*`_v0BeEu0KXpa2Y9w_LjEi8DeyVa9!Q}!w_Cc|!QmaX)Jvib zx5B$5gKY=cfG{8&XbH3eB7jH$A18)JZ0Ivf4dt@`P1^5o&&>97d2L22j2P%P`0Kc87 z0hoZAz%JzRixv-n-`KbU{6YmUU1Iswjl^$T_>B#}t+@*2^T27~F5rSZ4%B(<6A3p2zKg5s>Uh4$yL7C9nd3rP^({6v9iu3&3n(7El7r0G*aU0<*!l`!Bd`V73~U8n2DlEF?*e$LxL(m|B)b82*(reM z?IchMyaO0>%5%&OyapI$uRd1JWq$^+8fYAvLf;(kH9v>W+?*!PO$MYXOqSQO#wB&9B z&v#M8wldBEHEGwk-tixU`x9^xxBz?vdELgpRZa^7dkxmK=XSaf1!RAihKLY4Po8{1Isi}Gg*zAhZ}sNpe`w?T0fpAmU< zuO)sWw7efE@~GE+-^Ls+ZqQv9T->VVoUD_ox=&c7n@y4Om8E;I= zKbdeLhFge?NsNq^YN7^roD=q~xn;-$MHX?eArY;3%T(vba5+q-gdcMoEP@{QRN6Xi zVaVQgi4DXAnGXEeR=zLj`yXTEl`@4tESH^@oTkJ}R`Qd|9r@|(b1i>>)ODuzCO#Uv^S#r{4Sx@0(`3wpiC+%zX z9hEolRHjqmk;}4T;S+BhFP~{&H}cN=@&XRtnW$%d?i*fKf ze}1=zdo~A6S0^?a^NOS4ibbGz;{f%QD~H$Yh&hz#BBCN=5T}y#&|T>Rjd=CUHOAM^ z_CZUFzvot`hH)y}=l=cZey8KJoF&N~%37{#oa2t_SU!AT&AONqqjlq?x4U=eFV_BH z@;lCwX&%ZA9+`1AJbQogwz!((K67fU_fP_4v~HXx->`kgvtPRgpLc2)=goJdW)ID4 z-(t11f!Z6W5t3`E1Vj}8TOnJ8y;OO-QU4k(xk5PChA&@Q}3TIFSuRN z{`-SYjm~wIZ@6{spgh(Fmz`sVd$yhG)G&_H$9O*fO!a|&Bb_BP>ngSysB0X)KYG1% zo0oUje>gRaLr~XZ9Ka8X?i<-D?Z9HEMyQXngf)yK`=^qg9}zWa?$b_< zTptB@oUvM2_zXTTyklCNeq!QQg8{X=Q?9XcJ|G2?I6`&szs&kGfS6 z8R;VQWlb`U*B80;Yks$M9@YY*B(sU~2d--z$e%I)t!vlbxLfGdFplh>s@ZZ*?c(>b zMj3VYHc=j+uEjXy?;COc>1_vA9dK&gYNABdMC-<90neX%^x>3=?~ie67#|K~jk)yY z{A*V}a+Z`eS60_T>&7YTQJ31kHXtJ5x-1g(TlPeA<%3!xJm`9JZPotewo*@gj}km> z;t03W#8vpnJA;%cFVP?%2Hrb?WvT(nt_1qlcWdV%!>>`1*wSL8OF>EwYhMpiUT_sL z@y5aP{GMY6jOa9Tj;a|GX~k1+u=*rhoHjRbTH@aCPiSq#N5)GHf|ak)M!a#l{Z3W$ zh85dCZLey?@m6Piyf7QyMw|ddmJE#VK@Cr67Sn1;-d=xh~(OX^~tb9{fG&IHHxhYsFa6=1wgOzPe zj|3~9Fg+8j)T@majZYnN?sCVbGKtgeX&Mqh?x& zvXg0Mi1HWC9~YwBXL(A95?KebR_=Vt);X%7vYlq)Ep&x!6Nq;-=MAsF*->Ke1b9i=*RMuiX zWSuI-wS=GYfrp5*7@t<8G`zZ^?P1sFQ3o@~b>4|q0_viF50@d-_-dwN-=% z`ifO@S;P43V&uf2b}6;%Cg^RzF#&PPo6yihz{g^IfN^PF$V1!Ud1h$xEI@O6oDzgU z`}H7IZ7@EaUaoPfMXJrlcNkyQ3Gl5wGngO8csgL~?Y2s*`j52<-%qPA!d-79 zX&)>oN8Ciiz*siL_?F@Lps0Viz4ZEGG!m_a+MZ;ko)4zM2x5I*(bobW%aLU5tBBHl z$Btz;i=O9PUoIyrpLnC@gJflfmk5vl*XztL*Q$EL(oVVJB?9E>?Upw{Q$KIgM=oop z{KZFvH~kk)E7e}YU)j?@#KjvQk3_fM6g+*Fr=RLAH8PFQOq$An9oF~juP2~Hdq9^A z7yNt>mfv14^`%4~b^L!;w6(+YE1vy-<^JVJ#)RniZ@lqM%7Vp zR}QyYRh2Zrr-wRaF_BoYuu>&A!n}XjUYP)D@lDb0u>m7)Ji5Om{d=_yb=sGtC~q~w zDsd4SywJN0jOl;2=F7Yg^REw#Bea8ZyAjp^OGhOIlNoP(8#A|7*xFtjU)!qo8qLr6 z|9PZ5&^B0U9(7a}d1E|3^Iwc?xYdX#eKLQ?exx^qZD}WmrWRm7MZ@oCU{)6;(ihe! zUDOwP#cN9k?8+E-X8NYD2o3zIEB-R8s?XSO zy4%ZM28{q{+s{qTRqsR;nP*w<0po>P41?h{B`TDSQ?9PH?QtmggCk@eHvqY#bE8tae#QigOy}_jkG;+*@S++x7YC_NwoSaqNKaBmg^edFJ1a_p3;2jwzEzR z;|r&?yRPKiA2DFFv*cuVI|?(~apw{TMqT12c{ z0>3-NHPub2?=Lzk1N}vmp9>y|c#WQ&(B@q+)#D)^7gVJMgOm;a!m3>J7fMh{L3(!1 z6ny1~ud!P5KN{4Jh9fe^k8>6%fn9|~nb%a<6t@8JnsUS{+?AJ`iS0fq`~g>f>s)*Q zn4g}XACWa~WL9QoR;E%BAR6y`uDRG4urDH2EOb}i?;?inJMgq9^H)BaC|3Lj@w8nd diff --git a/services/frontend/components.json b/services/frontend/components.json new file mode 100644 index 0000000..cfbafe2 --- /dev/null +++ b/services/frontend/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "", + "css": "src/styles/globals.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} \ No newline at end of file diff --git a/services/frontend/package.json b/services/frontend/package.json index e3828a2..b216311 100644 --- a/services/frontend/package.json +++ b/services/frontend/package.json @@ -10,13 +10,21 @@ "preview": "vite preview" }, "dependencies": { + "@radix-ui/react-slot": "^1.1.2", "@tailwindcss/vite": "^4.0.9", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "lucide-react": "^0.476.0", + "ofetch": "^1.4.1", "react": "^19.0.0", "react-dom": "^19.0.0", - "tailwindcss": "^4.0.9" + "tailwind-merge": "^3.0.2", + "tailwindcss": "^4.0.9", + "tailwindcss-animate": "^1.0.7" }, "devDependencies": { "@eslint/js": "^9.21.0", + "@types/node": "^22.13.5", "@types/react": "^19.0.10", "@types/react-dom": "^19.0.4", "@vitejs/plugin-react-swc": "^3.8.0", @@ -28,6 +36,7 @@ "prettier-plugin-tailwindcss": "^0.6.11", "typescript": "~5.7.2", "typescript-eslint": "^8.24.1", - "vite": "^6.2.0" + "vite": "^6.2.0", + "vite-tsconfig-paths": "^5.1.4" } } diff --git a/services/frontend/src/components/ui/button.tsx b/services/frontend/src/components/ui/button.tsx new file mode 100644 index 0000000..e1b5d8a --- /dev/null +++ b/services/frontend/src/components/ui/button.tsx @@ -0,0 +1,58 @@ +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 gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-[color,box-shadow] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", + { + variants: { + variant: { + default: + "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90", + destructive: + "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40", + outline: + "border border-input bg-background shadow-xs hover:bg-accent hover:text-accent-foreground", + secondary: + "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80", + ghost: "hover:bg-accent hover:text-accent-foreground", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: "h-9 px-4 py-2 has-[>svg]:px-3", + sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5", + lg: "h-10 rounded-md px-6 has-[>svg]:px-4", + icon: "size-9", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +) + +function Button({ + className, + variant, + size, + asChild = false, + ...props +}: React.ComponentProps<"button"> & + VariantProps & { + asChild?: boolean + }) { + const Comp = asChild ? Slot : "button" + + return ( + + ) +} + +export { Button, buttonVariants } diff --git a/services/frontend/src/lib/utils.ts b/services/frontend/src/lib/utils.ts new file mode 100644 index 0000000..bd0c391 --- /dev/null +++ b/services/frontend/src/lib/utils.ts @@ -0,0 +1,6 @@ +import { clsx, type ClassValue } from "clsx" +import { twMerge } from "tailwind-merge" + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)) +} diff --git a/services/frontend/src/styles/globals.css b/services/frontend/src/styles/globals.css index f1d8c73..1f613d0 100644 --- a/services/frontend/src/styles/globals.css +++ b/services/frontend/src/styles/globals.css @@ -1 +1,124 @@ @import "tailwindcss"; + +@plugin "tailwindcss-animate"; + +@custom-variant dark (&:is(.dark *)); + +:root { + --background: oklch(1 0 0); + --foreground: oklch(0.145 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.145 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.145 0 0); + --primary: oklch(0.205 0 0); + --primary-foreground: oklch(0.985 0 0); + --secondary: oklch(0.97 0 0); + --secondary-foreground: oklch(0.205 0 0); + --muted: oklch(0.97 0 0); + --muted-foreground: oklch(0.556 0 0); + --accent: oklch(0.97 0 0); + --accent-foreground: oklch(0.205 0 0); + --destructive: oklch(0.577 0.245 27.325); + --destructive-foreground: oklch(0.577 0.245 27.325); + --border: oklch(0.922 0 0); + --input: oklch(0.922 0 0); + --ring: oklch(0.87 0 0); + --chart-1: oklch(0.646 0.222 41.116); + --chart-2: oklch(0.6 0.118 184.704); + --chart-3: oklch(0.398 0.07 227.392); + --chart-4: oklch(0.828 0.189 84.429); + --chart-5: oklch(0.769 0.188 70.08); + --radius: 0.625rem; + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.145 0 0); + --sidebar-primary: oklch(0.205 0 0); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.97 0 0); + --sidebar-accent-foreground: oklch(0.205 0 0); + --sidebar-border: oklch(0.922 0 0); + --sidebar-ring: oklch(0.87 0 0); +} + +.dark { + --background: oklch(0.145 0 0); + --foreground: oklch(0.985 0 0); + --card: oklch(0.145 0 0); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.145 0 0); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.985 0 0); + --primary-foreground: oklch(0.205 0 0); + --secondary: oklch(0.269 0 0); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.269 0 0); + --muted-foreground: oklch(0.708 0 0); + --accent: oklch(0.269 0 0); + --accent-foreground: oklch(0.985 0 0); + --destructive: oklch(0.396 0.141 25.723); + --destructive-foreground: oklch(0.637 0.237 25.331); + --border: oklch(0.269 0 0); + --input: oklch(0.269 0 0); + --ring: oklch(0.439 0 0); + --chart-1: oklch(0.488 0.243 264.376); + --chart-2: oklch(0.696 0.17 162.48); + --chart-3: oklch(0.769 0.188 70.08); + --chart-4: oklch(0.627 0.265 303.9); + --chart-5: oklch(0.645 0.246 16.439); + --sidebar: oklch(0.205 0 0); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.488 0.243 264.376); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.269 0 0); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(0.269 0 0); + --sidebar-ring: oklch(0.439 0 0); +} + +@theme inline { + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-card: var(--card); + --color-card-foreground: var(--card-foreground); + --color-popover: var(--popover); + --color-popover-foreground: var(--popover-foreground); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-foreground); + --color-destructive: var(--destructive); + --color-destructive-foreground: var(--destructive-foreground); + --color-border: var(--border); + --color-input: var(--input); + --color-ring: var(--ring); + --color-chart-1: var(--chart-1); + --color-chart-2: var(--chart-2); + --color-chart-3: var(--chart-3); + --color-chart-4: var(--chart-4); + --color-chart-5: var(--chart-5); + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); + --color-sidebar: var(--sidebar); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-ring: var(--sidebar-ring); +} + +@layer base { + * { + @apply border-border outline-ring/50; + } + body { + @apply bg-background text-foreground; + } +} diff --git a/services/frontend/tsconfig.app.json b/services/frontend/tsconfig.app.json index 358ca9b..0592374 100644 --- a/services/frontend/tsconfig.app.json +++ b/services/frontend/tsconfig.app.json @@ -20,7 +20,11 @@ "noUnusedLocals": true, "noUnusedParameters": true, "noFallthroughCasesInSwitch": true, - "noUncheckedSideEffectImports": true + "noUncheckedSideEffectImports": true, + + "paths": { + "@/*": ["./src/*"] + } }, "include": ["src"] } diff --git a/services/frontend/tsconfig.json b/services/frontend/tsconfig.json index 1ffef60..c36d52a 100644 --- a/services/frontend/tsconfig.json +++ b/services/frontend/tsconfig.json @@ -3,5 +3,10 @@ "references": [ { "path": "./tsconfig.app.json" }, { "path": "./tsconfig.node.json" } - ] + ], + "compilerOptions": { + "paths": { + "@/*": ["./src/*"] + } + } } diff --git a/services/frontend/vite.config.ts b/services/frontend/vite.config.ts index f961a45..75305ac 100644 --- a/services/frontend/vite.config.ts +++ b/services/frontend/vite.config.ts @@ -1,8 +1,9 @@ import { defineConfig } from "vite"; import react from "@vitejs/plugin-react-swc"; import tailwindcss from "@tailwindcss/vite"; +import tsconfigPaths from "vite-tsconfig-paths"; // https://vite.dev/config/ export default defineConfig({ - plugins: [react(), tailwindcss()], + plugins: [tsconfigPaths(), react(), tailwindcss()], });