From cc10146f7e8677db8a8404367b8caf33725cee2b Mon Sep 17 00:00:00 2001 From: Felitendo Date: Sun, 1 Jun 2025 03:13:14 +0200 Subject: [PATCH] initial commit --- .gitignore | 3 + DOCKERFILE | 25 + README.md | 55 +- components.json | 21 + eslint.config.js | 28 + index.html | 14 + package.json | 77 +++ public/felo-icon.svg | 10 + public/vite.svg | 1 + src/App.css | 42 ++ src/App.tsx | 777 ++++++++++++++++++++++++++ src/assets/react.svg | 1 + src/components/ui/accordion.tsx | 64 +++ src/components/ui/alert-dialog.tsx | 157 ++++++ src/components/ui/alert.tsx | 66 +++ src/components/ui/aspect-ratio.tsx | 9 + src/components/ui/avatar.tsx | 53 ++ src/components/ui/badge.tsx | 46 ++ src/components/ui/breadcrumb.tsx | 109 ++++ src/components/ui/button.tsx | 59 ++ src/components/ui/calendar.tsx | 73 +++ src/components/ui/card.tsx | 92 +++ src/components/ui/carousel.tsx | 241 ++++++++ src/components/ui/chart.tsx | 351 ++++++++++++ src/components/ui/checkbox.tsx | 32 ++ src/components/ui/collapsible.tsx | 31 + src/components/ui/command.tsx | 177 ++++++ src/components/ui/context-menu.tsx | 252 +++++++++ src/components/ui/dialog.tsx | 133 +++++ src/components/ui/drawer.tsx | 130 +++++ src/components/ui/dropdown-menu.tsx | 257 +++++++++ src/components/ui/form.tsx | 165 ++++++ src/components/ui/hover-card.tsx | 42 ++ src/components/ui/input-otp.tsx | 77 +++ src/components/ui/input.tsx | 21 + src/components/ui/label.tsx | 24 + src/components/ui/menubar.tsx | 274 +++++++++ src/components/ui/navigation-menu.tsx | 168 ++++++ src/components/ui/pagination.tsx | 127 +++++ src/components/ui/popover.tsx | 48 ++ src/components/ui/progress.tsx | 29 + src/components/ui/radio-group.tsx | 45 ++ src/components/ui/resizable.tsx | 54 ++ src/components/ui/scroll-area.tsx | 58 ++ src/components/ui/select.tsx | 183 ++++++ src/components/ui/separator.tsx | 28 + src/components/ui/sheet.tsx | 137 +++++ src/components/ui/sidebar.tsx | 726 ++++++++++++++++++++++++ src/components/ui/skeleton.tsx | 13 + src/components/ui/slider.tsx | 63 +++ src/components/ui/sonner.tsx | 23 + src/components/ui/switch.tsx | 31 + src/components/ui/table.tsx | 114 ++++ src/components/ui/tabs.tsx | 66 +++ src/components/ui/textarea.tsx | 18 + src/components/ui/toggle-group.tsx | 73 +++ src/components/ui/toggle.tsx | 45 ++ src/components/ui/tooltip.tsx | 59 ++ src/hooks/use-mobile.ts | 19 + src/index.css | 120 ++++ src/lib/utils.ts | 6 + src/main.tsx | 10 + src/vite-env.d.ts | 1 + tsconfig.app.json | 32 ++ tsconfig.json | 13 + tsconfig.node.json | 24 + vite.config.ts | 14 + 67 files changed, 6334 insertions(+), 2 deletions(-) create mode 100644 .gitignore create mode 100644 DOCKERFILE create mode 100644 components.json create mode 100644 eslint.config.js create mode 100644 index.html create mode 100644 package.json create mode 100644 public/felo-icon.svg create mode 100644 public/vite.svg create mode 100644 src/App.css create mode 100644 src/App.tsx create mode 100644 src/assets/react.svg create mode 100644 src/components/ui/accordion.tsx create mode 100644 src/components/ui/alert-dialog.tsx create mode 100644 src/components/ui/alert.tsx create mode 100644 src/components/ui/aspect-ratio.tsx create mode 100644 src/components/ui/avatar.tsx create mode 100644 src/components/ui/badge.tsx create mode 100644 src/components/ui/breadcrumb.tsx create mode 100644 src/components/ui/button.tsx create mode 100644 src/components/ui/calendar.tsx create mode 100644 src/components/ui/card.tsx create mode 100644 src/components/ui/carousel.tsx create mode 100644 src/components/ui/chart.tsx create mode 100644 src/components/ui/checkbox.tsx create mode 100644 src/components/ui/collapsible.tsx create mode 100644 src/components/ui/command.tsx create mode 100644 src/components/ui/context-menu.tsx create mode 100644 src/components/ui/dialog.tsx create mode 100644 src/components/ui/drawer.tsx create mode 100644 src/components/ui/dropdown-menu.tsx create mode 100644 src/components/ui/form.tsx create mode 100644 src/components/ui/hover-card.tsx create mode 100644 src/components/ui/input-otp.tsx create mode 100644 src/components/ui/input.tsx create mode 100644 src/components/ui/label.tsx create mode 100644 src/components/ui/menubar.tsx create mode 100644 src/components/ui/navigation-menu.tsx create mode 100644 src/components/ui/pagination.tsx create mode 100644 src/components/ui/popover.tsx create mode 100644 src/components/ui/progress.tsx create mode 100644 src/components/ui/radio-group.tsx create mode 100644 src/components/ui/resizable.tsx create mode 100644 src/components/ui/scroll-area.tsx create mode 100644 src/components/ui/select.tsx create mode 100644 src/components/ui/separator.tsx create mode 100644 src/components/ui/sheet.tsx create mode 100644 src/components/ui/sidebar.tsx create mode 100644 src/components/ui/skeleton.tsx create mode 100644 src/components/ui/slider.tsx create mode 100644 src/components/ui/sonner.tsx create mode 100644 src/components/ui/switch.tsx create mode 100644 src/components/ui/table.tsx create mode 100644 src/components/ui/tabs.tsx create mode 100644 src/components/ui/textarea.tsx create mode 100644 src/components/ui/toggle-group.tsx create mode 100644 src/components/ui/toggle.tsx create mode 100644 src/components/ui/tooltip.tsx create mode 100644 src/hooks/use-mobile.ts create mode 100644 src/index.css create mode 100644 src/lib/utils.ts create mode 100644 src/main.tsx create mode 100644 src/vite-env.d.ts create mode 100644 tsconfig.app.json create mode 100644 tsconfig.json create mode 100644 tsconfig.node.json create mode 100644 vite.config.ts diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d01fca5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +node_modules +package-lock.json +pnpm-lock.yaml diff --git a/DOCKERFILE b/DOCKERFILE new file mode 100644 index 0000000..2c1b87f --- /dev/null +++ b/DOCKERFILE @@ -0,0 +1,25 @@ +# Stage 1: Build with pnpm +FROM node:20-alpine AS builder + +WORKDIR /app + +# Install pnpm globally +RUN corepack enable && corepack prepare pnpm@latest --activate + +COPY package.json pnpm-lock.yaml ./ +RUN pnpm install + +COPY . . +RUN pnpm build + +# Stage 2: Serve with nginx +FROM nginx:alpine + +# Copy built files to nginx's web directory +COPY --from=builder /app/dist /usr/share/nginx/html + +# Expose port 3087 +EXPOSE 3087 + +# Change default nginx config to use port 3087 +RUN sed -i 's/80;/3087;/' /etc/nginx/conf.d/default.conf \ No newline at end of file diff --git a/README.md b/README.md index 71b9b55..da98444 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,54 @@ -# Website +# React + TypeScript + Vite -Website for https://store.felo.gg \ No newline at end of file +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) uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/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: + +```js +export default tseslint.config({ + extends: [ + // Remove ...tseslint.configs.recommended and replace with this + ...tseslint.configs.recommendedTypeChecked, + // Alternatively, use this for stricter rules + ...tseslint.configs.strictTypeChecked, + // Optionally, add this for stylistic rules + ...tseslint.configs.stylisticTypeChecked, + ], + languageOptions: { + // other options... + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + }, +}) +``` + +You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules: + +```js +// eslint.config.js +import reactX from 'eslint-plugin-react-x' +import reactDom from 'eslint-plugin-react-dom' + +export default tseslint.config({ + plugins: { + // Add the react-x and react-dom plugins + 'react-x': reactX, + 'react-dom': reactDom, + }, + rules: { + // other rules... + // Enable its recommended typescript rules + ...reactX.configs['recommended-typescript'].rules, + ...reactDom.configs.recommended.rules, + }, +}) +``` diff --git a/components.json b/components.json new file mode 100644 index 0000000..13e1db0 --- /dev/null +++ b/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "", + "css": "src/index.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..092408a --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,28 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' + +export default tseslint.config( + { ignores: ['dist'] }, + { + extends: [js.configs.recommended, ...tseslint.configs.recommended], + files: ['**/*.{ts,tsx}'], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + plugins: { + 'react-hooks': reactHooks, + 'react-refresh': reactRefresh, + }, + rules: { + ...reactHooks.configs.recommended.rules, + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, + }, +) diff --git a/index.html b/index.html new file mode 100644 index 0000000..e7040cf --- /dev/null +++ b/index.html @@ -0,0 +1,14 @@ + + + + + + + Felo Store - Premium App Features for Free + + + +
+ + + diff --git a/package.json b/package.json new file mode 100644 index 0000000..b5fc099 --- /dev/null +++ b/package.json @@ -0,0 +1,77 @@ +{ + "name": "vite-shadcn", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "@hookform/resolvers": "^5.0.1", + "@radix-ui/react-accordion": "^1.2.8", + "@radix-ui/react-alert-dialog": "^1.1.11", + "@radix-ui/react-aspect-ratio": "^1.1.4", + "@radix-ui/react-avatar": "^1.1.7", + "@radix-ui/react-checkbox": "^1.2.3", + "@radix-ui/react-collapsible": "^1.1.8", + "@radix-ui/react-context-menu": "^2.2.12", + "@radix-ui/react-dialog": "^1.1.11", + "@radix-ui/react-dropdown-menu": "^2.1.12", + "@radix-ui/react-hover-card": "^1.1.11", + "@radix-ui/react-label": "^2.1.4", + "@radix-ui/react-menubar": "^1.1.12", + "@radix-ui/react-navigation-menu": "^1.2.10", + "@radix-ui/react-popover": "^1.1.11", + "@radix-ui/react-progress": "^1.1.4", + "@radix-ui/react-radio-group": "^1.3.4", + "@radix-ui/react-scroll-area": "^1.2.6", + "@radix-ui/react-select": "^2.2.2", + "@radix-ui/react-separator": "^1.1.4", + "@radix-ui/react-slider": "^1.3.2", + "@radix-ui/react-slot": "^1.2.0", + "@radix-ui/react-switch": "^1.2.2", + "@radix-ui/react-tabs": "^1.1.9", + "@radix-ui/react-toggle": "^1.1.6", + "@radix-ui/react-toggle-group": "^1.1.7", + "@radix-ui/react-tooltip": "^1.2.4", + "@tailwindcss/vite": "^4.1.4", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "cmdk": "^1.1.1", + "date-fns": "^3.6.0", + "embla-carousel-react": "^8.6.0", + "framer-motion": "^12.15.0", + "input-otp": "^1.4.2", + "lucide-react": "^0.503.0", + "next-themes": "^0.4.6", + "react": "^18.3.1", + "react-day-picker": "^8.10.1", + "react-dom": "^18.3.1", + "react-hook-form": "^7.56.1", + "react-resizable-panels": "^2.1.9", + "recharts": "^2.15.3", + "sonner": "^2.0.3", + "tailwind-merge": "^3.2.0", + "tailwindcss": "^4.1.4", + "vaul": "^1.1.2", + "zod": "^3.24.3" + }, + "devDependencies": { + "@eslint/js": "^9.22.0", + "@types/node": "^22.15.3", + "@types/react": "^19.0.10", + "@types/react-dom": "^19.0.4", + "@vitejs/plugin-react": "^4.3.4", + "eslint": "^9.22.0", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.19", + "globals": "^16.0.0", + "tw-animate-css": "^1.2.8", + "typescript": "~5.7.2", + "typescript-eslint": "^8.26.1", + "vite": "^6.3.1" + } +} diff --git a/public/felo-icon.svg b/public/felo-icon.svg new file mode 100644 index 0000000..d19d6f8 --- /dev/null +++ b/public/felo-icon.svg @@ -0,0 +1,10 @@ + + + + + + + + + F + diff --git a/public/vite.svg b/public/vite.svg new file mode 100644 index 0000000..ee9fada --- /dev/null +++ b/public/vite.svg @@ -0,0 +1 @@ + diff --git a/src/App.css b/src/App.css new file mode 100644 index 0000000..b9d355d --- /dev/null +++ b/src/App.css @@ -0,0 +1,42 @@ +#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/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..5773546 --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,777 @@ +import { motion } from "framer-motion"; +import { Download, Github, Smartphone, Shield, RefreshCw, Star, ArrowRight, ExternalLink } from "lucide-react"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent } from "@/components/ui/card"; +import { useState, useEffect } from "react"; + +function App() { + const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 }); + + useEffect(() => { + const updateMousePosition = (e: MouseEvent) => { + setMousePosition({ x: e.clientX, y: e.clientY }); + }; + + window.addEventListener('mousemove', updateMousePosition); + return () => window.removeEventListener('mousemove', updateMousePosition); + }, []); + + const fadeInUp = { + initial: { opacity: 0, y: 60 }, + animate: { opacity: 1, y: 0 }, + transition: { duration: 0.6, ease: "easeOut" } + }; + + const staggerContainer = { + animate: { + transition: { + staggerChildren: 0.1 + } + } + }; + + const scaleOnHover = { + whileHover: { scale: 1.05 }, + whileTap: { scale: 0.95 } + }; + + const apps = [ + { + name: "Duolingo", + icon: "https://brandlogos.net/wp-content/uploads/2023/09/duolingo_icon-logo_brandlogos.net_aru6q-512x512.png", + features: ["Premium Unlocked", "No Ads", "No Telemetry"] + }, + { + name: "Telegram", + icon: "https://cdn.pixabay.com/photo/2021/12/27/10/50/telegram-6896827_1280.png", + features: ["Premium Unlocked", "No Ads", "No Telemetry"] + }, + { + name: "YouTube", + icon: "https://upload.wikimedia.org/wikipedia/commons/0/09/YouTube_full-color_icon_%282017%29.svg", + features: ["Premium Unlocked", "No Ads", "No Telemetry"] + }, + { + name: "Spotify", + icon: "https://upload.wikimedia.org/wikipedia/commons/8/84/Spotify_icon.svg", + features: ["Premium Unlocked", "No Ads", "No Telemetry"] + } + ]; + + const currentYear = new Date().getFullYear(); + + return ( +
+ {/* Interactive Mouse Cursor Background */} +
+ + {/* Animated Background */} +
+
+
+
+
+ + {/* Header */} + + + + + {/* Hero Section */} + + + + Felo Store + + + + Unlock premium features for your favorite apps like{" "} + + Telegram + {" "} + and{" "} + + Duolingo + {" "} + + completely free + + + + {/* Colorful Download App Button */} + + {/* Vibrant gradient background */} +
+ + {/* Glass overlay */} +
+ + {/* Top shine effect */} +
+ + {/* Animated shimmer */} +
+ + {/* Inner shadow for depth */} +
+ + {/* Content */} +
+ + + + + Download App + + + + +
+ + {/* Hover glow effect */} +
+
+ + {/* Colorful F-Droid Button */} + + {/* Vibrant gradient background */} +
+ + {/* Glass overlay */} +
+ + {/* Top shine effect */} +
+ + {/* Animated shimmer */} +
+ + {/* Inner shadow for depth */} +
+ + {/* Content */} +
+ + + + + Add to F-Droid + + + + +
+ + {/* Hover glow effect */} +
+
+
+
+
+ + {/* Features Section */} + +
+ + Why Choose Felo Store? + + + + + +
+ + + + + +

100% Safe

+

+ All apps are thoroughly tested and only fetched from trusted sites featured on freemediaheckyeah. +

+
+
+
+
+ + + +
+ + + + + +

Automatic Updates

+

+ Stay up-to-date with the latest features and security patches delivered automatically. +

+
+
+
+
+ + + +
+ + + + + +

Premium Quality

+

+ Access the same premium features you'd pay for, but completely free and fully functional. +

+
+
+
+
+
+
+
+ + {/* Apps Showcase */} + +
+ + Featured Apps + + + + {apps.map((app, index) => ( + + +
+ + + + {app.name} + +

{app.name}

+
+ {app.features.map((feature, idx) => ( +
+ + {feature} +
+ ))} +
+
+
+
+
+ ))} +
+ + {/* And Many More Text */} + + + ...and many more! + + + Discover hundreds of premium apps with unlocked features + + +
+
+ + {/* App Screenshots Section - Moved here after Featured Apps */} + +
+ + See It In Action + + + + Experience the clean, intuitive interface of Felo Store with our mobile app screenshots + + + + {/* Screenshot 1 - App List */} + + +
+
+
+ Felo Store App List + {/* Phone frame overlay */} +
+
+
+
+ + Browse Apps + + + Explore hundreds of premium apps with an intuitive, organized interface + +
+ + {/* Screenshot 2 - App Details */} + + +
+
+
+ Felo Store App Details + {/* Phone frame overlay */} +
+
+
+
+ + App Details + + + Get detailed information about each app before installation + +
+
+ + {/* Additional Info */} + + + + Available on Android devices + + +
+
+ + {/* CTA Section */} + +
+ + Ready to Get Started? + + + + Join thousands of users who are already enjoying premium app features for free + + + + {/* Colorful Download Now Button */} + + {/* Vibrant gradient background */} +
+ + {/* Glass overlay */} +
+ + {/* Top shine effect */} +
+ + {/* Animated shimmer */} +
+ + {/* Inner shadow for depth */} +
+ + {/* Content */} +
+ + + + + Download Now + + + + +
+ + {/* Hover glow effect */} +
+
+ + {/* Colorful F-Droid Repository Button */} + + {/* Vibrant gradient background */} +
+ + {/* Glass overlay */} +
+ + {/* Top shine effect */} +
+ + {/* Animated shimmer */} +
+ + {/* Inner shadow for depth */} +
+ + {/* Content */} +
+ + + + + Add to F-Droid + + + + +
+ + {/* Hover glow effect */} +
+
+
+
+
+ + {/* Footer */} + +
+
+
+
+ F +
+ Felo Store +
+ +
+ + + +
+
+ +
+

© {currentYear} Felo Store. Empowering users with free premium app access.

+
+
+
+ + +
+ ); +} + +export default App; diff --git a/src/assets/react.svg b/src/assets/react.svg new file mode 100644 index 0000000..8e0e0f1 --- /dev/null +++ b/src/assets/react.svg @@ -0,0 +1 @@ + diff --git a/src/components/ui/accordion.tsx b/src/components/ui/accordion.tsx new file mode 100644 index 0000000..d21b65f --- /dev/null +++ b/src/components/ui/accordion.tsx @@ -0,0 +1,64 @@ +import * as React from "react" +import * as AccordionPrimitive from "@radix-ui/react-accordion" +import { ChevronDownIcon } from "lucide-react" + +import { cn } from "@/lib/utils" + +function Accordion({ + ...props +}: React.ComponentProps) { + return +} + +function AccordionItem({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AccordionTrigger({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + + svg]:rotate-180", + className + )} + {...props} + > + {children} + + + + ) +} + +function AccordionContent({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + +
{children}
+
+ ) +} + +export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } diff --git a/src/components/ui/alert-dialog.tsx b/src/components/ui/alert-dialog.tsx new file mode 100644 index 0000000..0863e40 --- /dev/null +++ b/src/components/ui/alert-dialog.tsx @@ -0,0 +1,157 @@ +"use client" + +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" + +function AlertDialog({ + ...props +}: React.ComponentProps) { + return +} + +function AlertDialogTrigger({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogPortal({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogOverlay({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogContent({ + className, + ...props +}: React.ComponentProps) { + return ( + + + + + ) +} + +function AlertDialogHeader({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function AlertDialogFooter({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function AlertDialogTitle({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogDescription({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogAction({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogCancel({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { + AlertDialog, + AlertDialogPortal, + AlertDialogOverlay, + AlertDialogTrigger, + AlertDialogContent, + AlertDialogHeader, + AlertDialogFooter, + AlertDialogTitle, + AlertDialogDescription, + AlertDialogAction, + AlertDialogCancel, +} diff --git a/src/components/ui/alert.tsx b/src/components/ui/alert.tsx new file mode 100644 index 0000000..1421354 --- /dev/null +++ b/src/components/ui/alert.tsx @@ -0,0 +1,66 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const alertVariants = cva( + "relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current", + { + variants: { + variant: { + default: "bg-card text-card-foreground", + destructive: + "text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +function Alert({ + className, + variant, + ...props +}: React.ComponentProps<"div"> & VariantProps) { + return ( +
+ ) +} + +function AlertTitle({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function AlertDescription({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
+ ) +} + +export { Alert, AlertTitle, AlertDescription } diff --git a/src/components/ui/aspect-ratio.tsx b/src/components/ui/aspect-ratio.tsx new file mode 100644 index 0000000..9b491fb --- /dev/null +++ b/src/components/ui/aspect-ratio.tsx @@ -0,0 +1,9 @@ +import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio" + +function AspectRatio({ + ...props +}: React.ComponentProps) { + return +} + +export { AspectRatio } diff --git a/src/components/ui/avatar.tsx b/src/components/ui/avatar.tsx new file mode 100644 index 0000000..71e428b --- /dev/null +++ b/src/components/ui/avatar.tsx @@ -0,0 +1,53 @@ +"use client" + +import * as React from "react" +import * as AvatarPrimitive from "@radix-ui/react-avatar" + +import { cn } from "@/lib/utils" + +function Avatar({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AvatarImage({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AvatarFallback({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { Avatar, AvatarImage, AvatarFallback } diff --git a/src/components/ui/badge.tsx b/src/components/ui/badge.tsx new file mode 100644 index 0000000..0205413 --- /dev/null +++ b/src/components/ui/badge.tsx @@ -0,0 +1,46 @@ +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 badgeVariants = cva( + "inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-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 transition-[color,box-shadow] overflow-hidden", + { + variants: { + variant: { + default: + "border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90", + secondary: + "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90", + destructive: + "border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", + outline: + "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +function Badge({ + className, + variant, + asChild = false, + ...props +}: React.ComponentProps<"span"> & + VariantProps & { asChild?: boolean }) { + const Comp = asChild ? Slot : "span" + + return ( + + ) +} + +export { Badge, badgeVariants } diff --git a/src/components/ui/breadcrumb.tsx b/src/components/ui/breadcrumb.tsx new file mode 100644 index 0000000..eb88f32 --- /dev/null +++ b/src/components/ui/breadcrumb.tsx @@ -0,0 +1,109 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { ChevronRight, MoreHorizontal } from "lucide-react" + +import { cn } from "@/lib/utils" + +function Breadcrumb({ ...props }: React.ComponentProps<"nav">) { + return