From da62f5e72250207797ba074f8b17cd0d42f4837b Mon Sep 17 00:00:00 2001 From: Lucas Santana Date: Thu, 6 Feb 2025 21:26:46 -0300 Subject: [PATCH] =?UTF-8?q?refactor:=20atualiza=20dialog=20de=20confirma?= =?UTF-8?q?=C3=A7=C3=A3o=20para=20AlertDialog=20no=20EssayPage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Substitui Dialog básico pelo AlertDialog especializado - Melhora feedback visual na confirmação de deleção - Mantém consistência com o design system - Implementa padrões de acessibilidade do Radix UI --- package-lock.json | 274 ++++++++++++++++-- package.json | 3 + src/components/ui/alert-dialog.tsx | 139 +++++++++ src/components/ui/button.tsx | 53 ++-- src/lib/supabase.ts | 8 +- .../{[id]/analysis.tsx => EssayAnalysis.tsx} | 20 +- .../essays/{[id].tsx => EssayPage.tsx} | 118 ++++++-- .../essays/{new.tsx => NewEssay.tsx} | 30 +- src/pages/student-dashboard/essays/index.tsx | 36 ++- src/routes.tsx | 10 +- 10 files changed, 594 insertions(+), 97 deletions(-) create mode 100644 src/components/ui/alert-dialog.tsx rename src/pages/student-dashboard/essays/{[id]/analysis.tsx => EssayAnalysis.tsx} (93%) rename src/pages/student-dashboard/essays/{[id].tsx => EssayPage.tsx} (63%) rename src/pages/student-dashboard/essays/{new.tsx => NewEssay.tsx} (87%) diff --git a/package-lock.json b/package-lock.json index cb4047c..f7d1e69 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,8 +16,10 @@ "@opentelemetry/sdk-metrics": "^1.30.1", "@opentelemetry/sdk-trace-web": "^1.30.1", "@radix-ui/react-accordion": "^1.2.2", + "@radix-ui/react-alert-dialog": "^1.1.6", "@radix-ui/react-dialog": "^1.1.4", "@radix-ui/react-progress": "^1.1.1", + "@radix-ui/react-slot": "^1.1.2", "@radix-ui/react-tabs": "^1.1.2", "@radix-ui/react-toast": "^1.2.4", "@sentry/react": "^8.48.0", @@ -46,6 +48,7 @@ }, "devDependencies": { "@eslint/js": "^9.9.1", + "@shadcn/ui": "^0.0.4", "@testing-library/jest-dom": "^6.6.3", "@types/react": "^18.3.17", "@types/react-dom": "^18.3.5", @@ -2204,6 +2207,57 @@ } } }, + "node_modules/@radix-ui/react-alert-dialog": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.6.tgz", + "integrity": "sha512-p4XnPqgej8sZAAReCAKgz1REYZEBLR8hU9Pg27wFnCWIMc8g1ccCs0FjBcy05V15VTu8pAePw/VDYeOm/uZ6yQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-dialog": "1.1.6", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-slot": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-primitive": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.2.tgz", + "integrity": "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-collapsible": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.2.tgz", @@ -2260,6 +2314,24 @@ } } }, + "node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-slot": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.1.tgz", + "integrity": "sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-compose-refs": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz", @@ -2291,25 +2363,99 @@ } }, "node_modules/@radix-ui/react-dialog": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.4.tgz", - "integrity": "sha512-Ur7EV1IwQGCyaAuyDRiOLA5JIUZxELJljF+MbM/2NC0BYwfuRrbpS30BiQBJrVruscgUkieKkqXYDOoByaxIoA==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.6.tgz", + "integrity": "sha512-/IVhJV5AceX620DUJ4uYVMymzsipdKBzo3edo+omeskCKGm9FRHM0ebIdbPnlQVJqyuHbuBltQUOG2mOTq2IYw==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.3", + "@radix-ui/react-dismissable-layer": "1.1.5", "@radix-ui/react-focus-guards": "1.1.1", - "@radix-ui/react-focus-scope": "1.1.1", + "@radix-ui/react-focus-scope": "1.1.2", "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-portal": "1.1.3", + "@radix-ui/react-portal": "1.1.4", "@radix-ui/react-presence": "1.1.2", - "@radix-ui/react-primitive": "2.0.1", - "@radix-ui/react-slot": "1.1.1", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-slot": "1.1.2", "@radix-ui/react-use-controllable-state": "1.1.0", - "aria-hidden": "^1.1.1", - "react-remove-scroll": "^2.6.1" + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.5.tgz", + "integrity": "sha512-E4TywXY6UsXNRhFrECa5HAvE5/4BFcGyfTyK36gP+pAW1ed7UTK4vKwdr53gAJYwqbfCWC6ATvJa3J3R/9+Qrg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-escape-keydown": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-portal": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.4.tgz", + "integrity": "sha512-sn2O9k1rPFYVyKd5LAJfo96JlSGVFpa1fS6UuBJfrZadudiw5tAmru+n1x7aMRQ84qDM71Zh1+SzK5QwU0tJfA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-primitive": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.2.tgz", + "integrity": "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.1.2" }, "peerDependencies": { "@types/react": "*", @@ -2384,13 +2530,13 @@ } }, "node_modules/@radix-ui/react-focus-scope": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.1.tgz", - "integrity": "sha512-01omzJAYRxXdG2/he/+xy+c8a8gCydoQ1yOxnWNcRhrrBW5W+RQJ22EK1SaO8tb3WoUsuEw7mJjBozPzihDFjA==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.2.tgz", + "integrity": "sha512-zxwE80FCU7lcXUGWkdt6XpTTCKPitG1XKOwViTxHVKIJhZl9MvIl2dVHeZENCWD9+EdWv05wlaEkRXUykU27RA==", "license": "MIT", "dependencies": { "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-primitive": "2.0.1", + "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0" }, "peerDependencies": { @@ -2408,6 +2554,29 @@ } } }, + "node_modules/@radix-ui/react-focus-scope/node_modules/@radix-ui/react-primitive": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.2.tgz", + "integrity": "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-id": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.0.tgz", @@ -2497,6 +2666,24 @@ } } }, + "node_modules/@radix-ui/react-primitive/node_modules/@radix-ui/react-slot": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.1.tgz", + "integrity": "sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-progress": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.1.tgz", @@ -2553,9 +2740,9 @@ } }, "node_modules/@radix-ui/react-slot": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.1.tgz", - "integrity": "sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", + "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", "license": "MIT", "dependencies": { "@radix-ui/react-compose-refs": "1.1.1" @@ -3133,6 +3320,49 @@ "react": "^16.14.0 || 17.x || 18.x || 19.x" } }, + "node_modules/@shadcn/ui": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/@shadcn/ui/-/ui-0.0.4.tgz", + "integrity": "sha512-0dtu/5ApsOZ24qgaZwtif8jVwqol7a4m1x5AxPuM1k5wxhqU7t/qEfBGtaSki1R8VlbTQfCj5PAlO45NKCa7Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "5.2.0", + "commander": "^10.0.0", + "execa": "^7.0.0", + "fs-extra": "^11.1.0", + "node-fetch": "^3.3.0", + "ora": "^6.1.2", + "prompts": "^2.4.2", + "zod": "^3.20.2" + }, + "bin": { + "ui": "dist/index.js" + } + }, + "node_modules/@shadcn/ui/node_modules/chalk": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", + "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@shadcn/ui/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -8229,16 +8459,16 @@ } }, "node_modules/react-remove-scroll": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.2.tgz", - "integrity": "sha512-KmONPx5fnlXYJQqC62Q+lwIeAk64ws/cUw6omIumRzMRPqgnYqhSSti99nbj0Ry13bv7dF+BKn7NB+OqkdZGTw==", + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.3.tgz", + "integrity": "sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ==", "license": "MIT", "dependencies": { "react-remove-scroll-bar": "^2.3.7", - "react-style-singleton": "^2.2.1", + "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", - "use-sidecar": "^1.1.2" + "use-sidecar": "^1.1.3" }, "engines": { "node": ">=10" diff --git a/package.json b/package.json index e88baff..2a7f829 100644 --- a/package.json +++ b/package.json @@ -23,8 +23,10 @@ "@opentelemetry/sdk-metrics": "^1.30.1", "@opentelemetry/sdk-trace-web": "^1.30.1", "@radix-ui/react-accordion": "^1.2.2", + "@radix-ui/react-alert-dialog": "^1.1.6", "@radix-ui/react-dialog": "^1.1.4", "@radix-ui/react-progress": "^1.1.1", + "@radix-ui/react-slot": "^1.1.2", "@radix-ui/react-tabs": "^1.1.2", "@radix-ui/react-toast": "^1.2.4", "@sentry/react": "^8.48.0", @@ -53,6 +55,7 @@ }, "devDependencies": { "@eslint/js": "^9.9.1", + "@shadcn/ui": "^0.0.4", "@testing-library/jest-dom": "^6.6.3", "@types/react": "^18.3.17", "@types/react-dom": "^18.3.5", diff --git a/src/components/ui/alert-dialog.tsx b/src/components/ui/alert-dialog.tsx new file mode 100644 index 0000000..010910e --- /dev/null +++ b/src/components/ui/alert-dialog.tsx @@ -0,0 +1,139 @@ +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 = AlertDialogPrimitive.Portal + +const AlertDialogOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...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, + AlertDialogPortal, + AlertDialogOverlay, + AlertDialogTrigger, + AlertDialogContent, + AlertDialogHeader, + AlertDialogFooter, + AlertDialogTitle, + AlertDialogDescription, + AlertDialogAction, + AlertDialogCancel, +} \ No newline at end of file diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx index e869cbf..0b37483 100644 --- a/src/components/ui/button.tsx +++ b/src/components/ui/button.tsx @@ -7,11 +7,41 @@ import { EVENT_CATEGORIES } from '../../constants/analytics'; interface ButtonProps extends React.ButtonHTMLAttributes { as?: 'button' | 'span'; trackingId: string; - variant?: 'default' | 'primary' | 'secondary' | 'outline' | 'ghost' | 'link'; + variant?: 'default' | 'primary' | 'secondary' | 'outline' | 'ghost' | 'link' | 'destructive'; size?: 'sm' | 'md' | 'lg'; trackingProperties?: ButtonTrackingOptions; } +export function buttonVariants({ + variant = 'default', + size = 'md', + className = '', +}: { + variant?: ButtonProps['variant']; + size?: ButtonProps['size']; + className?: string; +} = {}) { + return cn( + 'inline-flex items-center justify-center px-4 py-2', + 'text-sm font-medium', + 'rounded-md shadow-sm', + 'transition-colors duration-200', + 'disabled:opacity-50 disabled:cursor-not-allowed', + { + 'text-white bg-purple-600 hover:bg-purple-700': variant === 'primary' || variant === 'default', + 'text-gray-700 bg-white border border-gray-300 hover:bg-gray-50': variant === 'secondary', + 'text-purple-600 bg-transparent hover:bg-purple-50': variant === 'ghost', + 'text-purple-600 bg-transparent hover:underline': variant === 'link', + 'text-purple-600 border border-purple-600 hover:bg-purple-50': variant === 'outline', + 'text-white bg-red-600 hover:bg-red-700': variant === 'destructive', + 'px-3 py-1.5 text-sm': size === 'sm', + 'px-4 py-2 text-base': size === 'md', + 'px-6 py-3 text-lg': size === 'lg', + }, + className + ); +} + export function Button({ as: Component = 'button', children, @@ -41,29 +71,10 @@ export function Button({ onClick?.(event); }; - const baseStyles = cn( - 'inline-flex items-center justify-center px-4 py-2', - 'text-sm font-medium', - 'rounded-md shadow-sm', - 'transition-colors duration-200', - 'disabled:opacity-50 disabled:cursor-not-allowed', - { - 'text-white bg-purple-600 hover:bg-purple-700': variant === 'primary' || variant === 'default', - 'text-gray-700 bg-white border border-gray-300 hover:bg-gray-50': variant === 'secondary', - 'text-purple-600 bg-transparent hover:bg-purple-50': variant === 'ghost', - 'text-purple-600 bg-transparent hover:underline': variant === 'link', - 'text-purple-600 border border-purple-600 hover:bg-purple-50': variant === 'outline', - 'px-3 py-1.5 text-sm': size === 'sm', - 'px-4 py-2 text-base': size === 'md', - 'px-6 py-3 text-lg': size === 'lg', - }, - className - ); - return ( { const { data: { session } } = await supabase.auth.getSession() diff --git a/src/pages/student-dashboard/essays/[id]/analysis.tsx b/src/pages/student-dashboard/essays/EssayAnalysis.tsx similarity index 93% rename from src/pages/student-dashboard/essays/[id]/analysis.tsx rename to src/pages/student-dashboard/essays/EssayAnalysis.tsx index 0a93fef..4f69d01 100644 --- a/src/pages/student-dashboard/essays/[id]/analysis.tsx +++ b/src/pages/student-dashboard/essays/EssayAnalysis.tsx @@ -1,5 +1,5 @@ import { useEffect, useState } from 'react'; -import { useRouter } from 'next/router'; +import { useParams, useNavigate } from 'react-router-dom'; import { supabase } from '@/lib/supabase'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; @@ -40,10 +40,9 @@ interface Essay { }; } -export default function EssayAnalysisPage() { - const router = useRouter(); - const { id } = router.query; - const { supabase } = supabase(); +export function EssayAnalysis() { + const navigate = useNavigate(); + const { id } = useParams(); const [analysis, setAnalysis] = useState(null); const [essay, setEssay] = useState(null); const [loading, setLoading] = useState(true); @@ -94,7 +93,16 @@ export default function EssayAnalysisPage() { return (
- diff --git a/src/pages/student-dashboard/essays/[id].tsx b/src/pages/student-dashboard/essays/EssayPage.tsx similarity index 63% rename from src/pages/student-dashboard/essays/[id].tsx rename to src/pages/student-dashboard/essays/EssayPage.tsx index 57eb04c..f1faa1d 100644 --- a/src/pages/student-dashboard/essays/[id].tsx +++ b/src/pages/student-dashboard/essays/EssayPage.tsx @@ -1,12 +1,22 @@ import { useEffect, useState } from 'react'; -import { useRouter } from 'next/router'; +import { useParams, useNavigate } from 'react-router-dom'; import { supabase } from '@/lib/supabase'; import { Button } from '@/components/ui/button'; import { Card, CardContent } from '@/components/ui/card'; import { Input } from '@/components/ui/input'; import { Textarea } from '@/components/ui/textarea'; -import { ArrowLeft, Save, Send } from 'lucide-react'; +import { ArrowLeft, Save, Send, Trash2 } from 'lucide-react'; import { Badge } from '@/components/ui/badge'; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from "@/components/ui/alert-dialog"; interface Essay { id: string; @@ -30,14 +40,14 @@ interface Essay { }; } -export default function EssayPage() { - const router = useRouter(); - const { id } = router.query; - const { supabase } = supabase(); +export function EssayPage() { + const navigate = useNavigate(); + const { id } = useParams(); const [essay, setEssay] = useState(null); const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); const [wordCount, setWordCount] = useState(0); + const [showDeleteDialog, setShowDeleteDialog] = useState(false); useEffect(() => { if (id) { @@ -117,12 +127,27 @@ export default function EssayPage() { if (analysisError) throw analysisError; // Redireciona para a página de análise - router.push(`/student-dashboard/essays/${essay.id}/analysis`); + navigate(`/aluno/redacoes/${essay.id}/analise`); } catch (error) { console.error('Erro ao enviar para análise:', error); } } + async function deleteEssay() { + if (!essay) return; + try { + const { error } = await supabase + .from('student_essays') + .delete() + .eq('id', essay.id); + + if (error) throw error; + navigate('/aluno/redacoes'); + } catch (error) { + console.error('Erro ao deletar redação:', error); + } + } + if (loading) return
Carregando...
; if (!essay) return
Redação não encontrada
; @@ -134,7 +159,15 @@ export default function EssayPage() {
- @@ -150,26 +183,58 @@ export default function EssayPage() { {essay.essay_genre.title} - + {essay.status === 'draft' ? 'Rascunho' : 'Enviada'}
- {essay.status === 'draft' && ( - + <> + + + )}
@@ -209,6 +274,23 @@ export default function EssayPage() {
+ + + + + Você tem certeza? + + Esta ação não pode ser desfeita. Isso excluirá permanentemente sua redação. + + + + Cancelar + + Deletar + + + +
); } \ No newline at end of file diff --git a/src/pages/student-dashboard/essays/new.tsx b/src/pages/student-dashboard/essays/NewEssay.tsx similarity index 87% rename from src/pages/student-dashboard/essays/new.tsx rename to src/pages/student-dashboard/essays/NewEssay.tsx index 90b9b95..53bf70c 100644 --- a/src/pages/student-dashboard/essays/new.tsx +++ b/src/pages/student-dashboard/essays/NewEssay.tsx @@ -1,5 +1,5 @@ -import { useState } from 'react'; -import { useRouter } from 'next/router'; +import { useState, useEffect } from 'react'; +import { useNavigate } from 'react-router-dom'; import { supabase } from '@/lib/supabase'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; @@ -24,15 +24,18 @@ interface EssayGenre { }; } -export default function NewEssayPage() { - const router = useRouter(); - const { supabase } = supabase(); +export function NewEssay() { + const navigate = useNavigate(); const [step, setStep] = useState<'type' | 'genre'>('type'); const [selectedType, setSelectedType] = useState(null); const [types, setTypes] = useState([]); const [genres, setGenres] = useState([]); const [loading, setLoading] = useState(true); + useEffect(() => { + loadTypes(); + }, []); + // Carregar tipos textuais async function loadTypes() { try { @@ -69,9 +72,13 @@ export default function NewEssayPage() { // Criar nova redação async function createEssay(genreId: string) { try { + const { data: { user } } = await supabase.auth.getUser(); + if (!user) throw new Error('Usuário não autenticado'); + const { data, error } = await supabase .from('student_essays') .insert({ + student_id: user.id, type_id: selectedType!.id, genre_id: genreId, status: 'draft', @@ -82,7 +89,7 @@ export default function NewEssayPage() { .single(); if (error) throw error; - router.push(`/student-dashboard/essays/${data.id}`); + navigate(`/aluno/redacoes/${data.id}`); } catch (error) { console.error('Erro ao criar redação:', error); } @@ -156,7 +163,16 @@ export default function NewEssayPage() {
{step === 'genre' && ( - diff --git a/src/pages/student-dashboard/essays/index.tsx b/src/pages/student-dashboard/essays/index.tsx index b98674e..598fa8f 100644 --- a/src/pages/student-dashboard/essays/index.tsx +++ b/src/pages/student-dashboard/essays/index.tsx @@ -1,9 +1,9 @@ import { useEffect, useState } from 'react'; +import { useNavigate } from 'react-router-dom'; import { supabase } from '@/lib/supabase'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { PlusCircle } from 'lucide-react'; -import { useRouter } from 'next/router'; import { Badge } from '@/components/ui/badge'; interface Essay { @@ -21,9 +21,8 @@ interface Essay { }; } -export default function EssaysPage() { - const router = useRouter(); - const { supabase } = supabase(); +export function EssaysPage() { + const navigate = useNavigate(); const [essays, setEssays] = useState([]); const [loading, setLoading] = useState(true); @@ -53,20 +52,27 @@ export default function EssaysPage() { function getStatusBadge(status: Essay['status']) { const statusMap = { - draft: { label: 'Rascunho', variant: 'secondary' }, - submitted: { label: 'Enviada', variant: 'primary' }, - analyzed: { label: 'Analisada', variant: 'success' } + draft: { label: 'Rascunho', variant: 'secondary' as const }, + submitted: { label: 'Enviada', variant: 'default' as const }, + analyzed: { label: 'Analisada', variant: 'success' as const } }; const { label, variant } = statusMap[status]; - return {label}; + return {label}; } return (

Minhas Redações

- @@ -78,7 +84,15 @@ export default function EssaysPage() {

Você ainda não tem nenhuma redação

-
@@ -87,7 +101,7 @@ export default function EssaysPage() {
{essays.map((essay) => ( router.push(`/student-dashboard/essays/${essay.id}`)}> + onClick={() => navigate(`/aluno/redacoes/${essay.id}`)}>
diff --git a/src/routes.tsx b/src/routes.tsx index c82ee07..7c05af9 100644 --- a/src/routes.tsx +++ b/src/routes.tsx @@ -34,9 +34,9 @@ import { TextSalesLetter } from './pages/landing/TextSalesLetter'; import { PhonicsPage } from "./pages/student-dashboard/PhonicsPage"; import { PhonicsProgressPage } from "./pages/student-dashboard/PhonicsProgressPage"; import { EssaysPage } from './pages/student-dashboard/essays'; -import { NewEssayPage } from './pages/student-dashboard/essays/new'; -import { EssayPage } from './pages/student-dashboard/essays/[id]'; -import { EssayAnalysisPage } from './pages/student-dashboard/essays/[id]/analysis'; +import { NewEssay } from './pages/student-dashboard/essays/NewEssay'; +import { EssayPage } from './pages/student-dashboard/essays/EssayPage'; +import { EssayAnalysis } from './pages/student-dashboard/essays/EssayAnalysis'; function RootLayout({ children }: { children: React.ReactNode }) { return ( @@ -233,7 +233,7 @@ export const router = createBrowserRouter([ }, { path: 'nova', - element: , + element: , }, { path: ':id', @@ -241,7 +241,7 @@ export const router = createBrowserRouter([ }, { path: ':id/analise', - element: , + element: , } ] }