mirror of
https://github.com/lucasrcsantana/story-generator.git
synced 2025-12-17 22:07:52 +00:00
refactor: atualiza dialog de confirmação para AlertDialog no EssayPage
- 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
This commit is contained in:
parent
d1e44f84b7
commit
da62f5e722
274
package-lock.json
generated
274
package-lock.json
generated
@ -16,8 +16,10 @@
|
|||||||
"@opentelemetry/sdk-metrics": "^1.30.1",
|
"@opentelemetry/sdk-metrics": "^1.30.1",
|
||||||
"@opentelemetry/sdk-trace-web": "^1.30.1",
|
"@opentelemetry/sdk-trace-web": "^1.30.1",
|
||||||
"@radix-ui/react-accordion": "^1.2.2",
|
"@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-dialog": "^1.1.4",
|
||||||
"@radix-ui/react-progress": "^1.1.1",
|
"@radix-ui/react-progress": "^1.1.1",
|
||||||
|
"@radix-ui/react-slot": "^1.1.2",
|
||||||
"@radix-ui/react-tabs": "^1.1.2",
|
"@radix-ui/react-tabs": "^1.1.2",
|
||||||
"@radix-ui/react-toast": "^1.2.4",
|
"@radix-ui/react-toast": "^1.2.4",
|
||||||
"@sentry/react": "^8.48.0",
|
"@sentry/react": "^8.48.0",
|
||||||
@ -46,6 +48,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.9.1",
|
"@eslint/js": "^9.9.1",
|
||||||
|
"@shadcn/ui": "^0.0.4",
|
||||||
"@testing-library/jest-dom": "^6.6.3",
|
"@testing-library/jest-dom": "^6.6.3",
|
||||||
"@types/react": "^18.3.17",
|
"@types/react": "^18.3.17",
|
||||||
"@types/react-dom": "^18.3.5",
|
"@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": {
|
"node_modules/@radix-ui/react-collapsible": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.2.tgz",
|
"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": {
|
"node_modules/@radix-ui/react-compose-refs": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz",
|
"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": {
|
"node_modules/@radix-ui/react-dialog": {
|
||||||
"version": "1.1.4",
|
"version": "1.1.6",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.6.tgz",
|
||||||
"integrity": "sha512-Ur7EV1IwQGCyaAuyDRiOLA5JIUZxELJljF+MbM/2NC0BYwfuRrbpS30BiQBJrVruscgUkieKkqXYDOoByaxIoA==",
|
"integrity": "sha512-/IVhJV5AceX620DUJ4uYVMymzsipdKBzo3edo+omeskCKGm9FRHM0ebIdbPnlQVJqyuHbuBltQUOG2mOTq2IYw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@radix-ui/primitive": "1.1.1",
|
"@radix-ui/primitive": "1.1.1",
|
||||||
"@radix-ui/react-compose-refs": "1.1.1",
|
"@radix-ui/react-compose-refs": "1.1.1",
|
||||||
"@radix-ui/react-context": "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-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-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-presence": "1.1.2",
|
||||||
"@radix-ui/react-primitive": "2.0.1",
|
"@radix-ui/react-primitive": "2.0.2",
|
||||||
"@radix-ui/react-slot": "1.1.1",
|
"@radix-ui/react-slot": "1.1.2",
|
||||||
"@radix-ui/react-use-controllable-state": "1.1.0",
|
"@radix-ui/react-use-controllable-state": "1.1.0",
|
||||||
"aria-hidden": "^1.1.1",
|
"aria-hidden": "^1.2.4",
|
||||||
"react-remove-scroll": "^2.6.1"
|
"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": {
|
"peerDependencies": {
|
||||||
"@types/react": "*",
|
"@types/react": "*",
|
||||||
@ -2384,13 +2530,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@radix-ui/react-focus-scope": {
|
"node_modules/@radix-ui/react-focus-scope": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.2.tgz",
|
||||||
"integrity": "sha512-01omzJAYRxXdG2/he/+xy+c8a8gCydoQ1yOxnWNcRhrrBW5W+RQJ22EK1SaO8tb3WoUsuEw7mJjBozPzihDFjA==",
|
"integrity": "sha512-zxwE80FCU7lcXUGWkdt6XpTTCKPitG1XKOwViTxHVKIJhZl9MvIl2dVHeZENCWD9+EdWv05wlaEkRXUykU27RA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@radix-ui/react-compose-refs": "1.1.1",
|
"@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"
|
"@radix-ui/react-use-callback-ref": "1.1.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"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": {
|
"node_modules/@radix-ui/react-id": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.0.tgz",
|
"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": {
|
"node_modules/@radix-ui/react-progress": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.1.tgz",
|
"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": {
|
"node_modules/@radix-ui/react-slot": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz",
|
||||||
"integrity": "sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==",
|
"integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@radix-ui/react-compose-refs": "1.1.1"
|
"@radix-ui/react-compose-refs": "1.1.1"
|
||||||
@ -3133,6 +3320,49 @@
|
|||||||
"react": "^16.14.0 || 17.x || 18.x || 19.x"
|
"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": {
|
"node_modules/@sinclair/typebox": {
|
||||||
"version": "0.27.8",
|
"version": "0.27.8",
|
||||||
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz",
|
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz",
|
||||||
@ -8229,16 +8459,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-remove-scroll": {
|
"node_modules/react-remove-scroll": {
|
||||||
"version": "2.6.2",
|
"version": "2.6.3",
|
||||||
"resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.3.tgz",
|
||||||
"integrity": "sha512-KmONPx5fnlXYJQqC62Q+lwIeAk64ws/cUw6omIumRzMRPqgnYqhSSti99nbj0Ry13bv7dF+BKn7NB+OqkdZGTw==",
|
"integrity": "sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"react-remove-scroll-bar": "^2.3.7",
|
"react-remove-scroll-bar": "^2.3.7",
|
||||||
"react-style-singleton": "^2.2.1",
|
"react-style-singleton": "^2.2.3",
|
||||||
"tslib": "^2.1.0",
|
"tslib": "^2.1.0",
|
||||||
"use-callback-ref": "^1.3.3",
|
"use-callback-ref": "^1.3.3",
|
||||||
"use-sidecar": "^1.1.2"
|
"use-sidecar": "^1.1.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
|
|||||||
@ -23,8 +23,10 @@
|
|||||||
"@opentelemetry/sdk-metrics": "^1.30.1",
|
"@opentelemetry/sdk-metrics": "^1.30.1",
|
||||||
"@opentelemetry/sdk-trace-web": "^1.30.1",
|
"@opentelemetry/sdk-trace-web": "^1.30.1",
|
||||||
"@radix-ui/react-accordion": "^1.2.2",
|
"@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-dialog": "^1.1.4",
|
||||||
"@radix-ui/react-progress": "^1.1.1",
|
"@radix-ui/react-progress": "^1.1.1",
|
||||||
|
"@radix-ui/react-slot": "^1.1.2",
|
||||||
"@radix-ui/react-tabs": "^1.1.2",
|
"@radix-ui/react-tabs": "^1.1.2",
|
||||||
"@radix-ui/react-toast": "^1.2.4",
|
"@radix-ui/react-toast": "^1.2.4",
|
||||||
"@sentry/react": "^8.48.0",
|
"@sentry/react": "^8.48.0",
|
||||||
@ -53,6 +55,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.9.1",
|
"@eslint/js": "^9.9.1",
|
||||||
|
"@shadcn/ui": "^0.0.4",
|
||||||
"@testing-library/jest-dom": "^6.6.3",
|
"@testing-library/jest-dom": "^6.6.3",
|
||||||
"@types/react": "^18.3.17",
|
"@types/react": "^18.3.17",
|
||||||
"@types/react-dom": "^18.3.5",
|
"@types/react-dom": "^18.3.5",
|
||||||
|
|||||||
139
src/components/ui/alert-dialog.tsx
Normal file
139
src/components/ui/alert-dialog.tsx
Normal file
@ -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<typeof AlertDialogPrimitive.Overlay>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<AlertDialogPrimitive.Overlay
|
||||||
|
className={cn(
|
||||||
|
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
ref={ref}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName
|
||||||
|
|
||||||
|
const AlertDialogContent = React.forwardRef<
|
||||||
|
React.ElementRef<typeof AlertDialogPrimitive.Content>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<AlertDialogPortal>
|
||||||
|
<AlertDialogOverlay />
|
||||||
|
<AlertDialogPrimitive.Content
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
</AlertDialogPortal>
|
||||||
|
))
|
||||||
|
AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName
|
||||||
|
|
||||||
|
const AlertDialogHeader = ({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"flex flex-col space-y-2 text-center sm:text-left",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
AlertDialogHeader.displayName = "AlertDialogHeader"
|
||||||
|
|
||||||
|
const AlertDialogFooter = ({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
AlertDialogFooter.displayName = "AlertDialogFooter"
|
||||||
|
|
||||||
|
const AlertDialogTitle = React.forwardRef<
|
||||||
|
React.ElementRef<typeof AlertDialogPrimitive.Title>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<AlertDialogPrimitive.Title
|
||||||
|
ref={ref}
|
||||||
|
className={cn("text-lg font-semibold", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName
|
||||||
|
|
||||||
|
const AlertDialogDescription = React.forwardRef<
|
||||||
|
React.ElementRef<typeof AlertDialogPrimitive.Description>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<AlertDialogPrimitive.Description
|
||||||
|
ref={ref}
|
||||||
|
className={cn("text-sm text-muted-foreground", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
AlertDialogDescription.displayName =
|
||||||
|
AlertDialogPrimitive.Description.displayName
|
||||||
|
|
||||||
|
const AlertDialogAction = React.forwardRef<
|
||||||
|
React.ElementRef<typeof AlertDialogPrimitive.Action>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<AlertDialogPrimitive.Action
|
||||||
|
ref={ref}
|
||||||
|
className={cn(buttonVariants(), className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName
|
||||||
|
|
||||||
|
const AlertDialogCancel = React.forwardRef<
|
||||||
|
React.ElementRef<typeof AlertDialogPrimitive.Cancel>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<AlertDialogPrimitive.Cancel
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
buttonVariants({ variant: "outline" }),
|
||||||
|
"mt-2 sm:mt-0",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName
|
||||||
|
|
||||||
|
export {
|
||||||
|
AlertDialog,
|
||||||
|
AlertDialogPortal,
|
||||||
|
AlertDialogOverlay,
|
||||||
|
AlertDialogTrigger,
|
||||||
|
AlertDialogContent,
|
||||||
|
AlertDialogHeader,
|
||||||
|
AlertDialogFooter,
|
||||||
|
AlertDialogTitle,
|
||||||
|
AlertDialogDescription,
|
||||||
|
AlertDialogAction,
|
||||||
|
AlertDialogCancel,
|
||||||
|
}
|
||||||
@ -7,11 +7,41 @@ import { EVENT_CATEGORIES } from '../../constants/analytics';
|
|||||||
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||||
as?: 'button' | 'span';
|
as?: 'button' | 'span';
|
||||||
trackingId: string;
|
trackingId: string;
|
||||||
variant?: 'default' | 'primary' | 'secondary' | 'outline' | 'ghost' | 'link';
|
variant?: 'default' | 'primary' | 'secondary' | 'outline' | 'ghost' | 'link' | 'destructive';
|
||||||
size?: 'sm' | 'md' | 'lg';
|
size?: 'sm' | 'md' | 'lg';
|
||||||
trackingProperties?: ButtonTrackingOptions;
|
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({
|
export function Button({
|
||||||
as: Component = 'button',
|
as: Component = 'button',
|
||||||
children,
|
children,
|
||||||
@ -41,29 +71,10 @@ export function Button({
|
|||||||
onClick?.(event);
|
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 (
|
return (
|
||||||
<Component
|
<Component
|
||||||
type={Component === 'button' ? type : undefined}
|
type={Component === 'button' ? type : undefined}
|
||||||
className={baseStyles}
|
className={buttonVariants({ variant, size, className })}
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@ -8,13 +8,7 @@ if (!supabaseUrl || !supabaseAnonKey) {
|
|||||||
throw new Error('Variáveis de ambiente do Supabase não configuradas')
|
throw new Error('Variáveis de ambiente do Supabase não configuradas')
|
||||||
}
|
}
|
||||||
|
|
||||||
export const supabase = createClient(supabaseUrl, supabaseAnonKey, {
|
export const supabase = createClient(supabaseUrl, supabaseAnonKey)
|
||||||
auth: {
|
|
||||||
autoRefreshToken: true,
|
|
||||||
persistSession: true,
|
|
||||||
detectSessionInUrl: true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export const generateStoryFunction = async (prompt: StoryPrompt) => {
|
export const generateStoryFunction = async (prompt: StoryPrompt) => {
|
||||||
const { data: { session } } = await supabase.auth.getSession()
|
const { data: { session } } = await supabase.auth.getSession()
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useRouter } from 'next/router';
|
import { useParams, useNavigate } from 'react-router-dom';
|
||||||
import { supabase } from '@/lib/supabase';
|
import { supabase } from '@/lib/supabase';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
@ -40,10 +40,9 @@ interface Essay {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function EssayAnalysisPage() {
|
export function EssayAnalysis() {
|
||||||
const router = useRouter();
|
const navigate = useNavigate();
|
||||||
const { id } = router.query;
|
const { id } = useParams();
|
||||||
const { supabase } = supabase();
|
|
||||||
const [analysis, setAnalysis] = useState<EssayAnalysis | null>(null);
|
const [analysis, setAnalysis] = useState<EssayAnalysis | null>(null);
|
||||||
const [essay, setEssay] = useState<Essay | null>(null);
|
const [essay, setEssay] = useState<Essay | null>(null);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
@ -94,7 +93,16 @@ export default function EssayAnalysisPage() {
|
|||||||
return (
|
return (
|
||||||
<div className="container mx-auto p-6">
|
<div className="container mx-auto p-6">
|
||||||
<div className="flex items-center gap-4 mb-6">
|
<div className="flex items-center gap-4 mb-6">
|
||||||
<Button variant="ghost" onClick={() => router.push(`/student-dashboard/essays/${id}`)}>
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
onClick={() => navigate(`/aluno/redacoes/${id}`)}
|
||||||
|
trackingId="essay-analysis-back-button"
|
||||||
|
trackingProperties={{
|
||||||
|
action: 'back_to_essay',
|
||||||
|
page: 'essay_analysis',
|
||||||
|
essay_id: id
|
||||||
|
}}
|
||||||
|
>
|
||||||
<ArrowLeft className="mr-2 h-4 w-4" />
|
<ArrowLeft className="mr-2 h-4 w-4" />
|
||||||
Voltar para redação
|
Voltar para redação
|
||||||
</Button>
|
</Button>
|
||||||
@ -1,12 +1,22 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useRouter } from 'next/router';
|
import { useParams, useNavigate } from 'react-router-dom';
|
||||||
import { supabase } from '@/lib/supabase';
|
import { supabase } from '@/lib/supabase';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Card, CardContent } from '@/components/ui/card';
|
import { Card, CardContent } from '@/components/ui/card';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import { Textarea } from '@/components/ui/textarea';
|
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 { Badge } from '@/components/ui/badge';
|
||||||
|
import {
|
||||||
|
AlertDialog,
|
||||||
|
AlertDialogAction,
|
||||||
|
AlertDialogCancel,
|
||||||
|
AlertDialogContent,
|
||||||
|
AlertDialogDescription,
|
||||||
|
AlertDialogFooter,
|
||||||
|
AlertDialogHeader,
|
||||||
|
AlertDialogTitle,
|
||||||
|
} from "@/components/ui/alert-dialog";
|
||||||
|
|
||||||
interface Essay {
|
interface Essay {
|
||||||
id: string;
|
id: string;
|
||||||
@ -30,14 +40,14 @@ interface Essay {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function EssayPage() {
|
export function EssayPage() {
|
||||||
const router = useRouter();
|
const navigate = useNavigate();
|
||||||
const { id } = router.query;
|
const { id } = useParams();
|
||||||
const { supabase } = supabase();
|
|
||||||
const [essay, setEssay] = useState<Essay | null>(null);
|
const [essay, setEssay] = useState<Essay | null>(null);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [saving, setSaving] = useState(false);
|
const [saving, setSaving] = useState(false);
|
||||||
const [wordCount, setWordCount] = useState(0);
|
const [wordCount, setWordCount] = useState(0);
|
||||||
|
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (id) {
|
if (id) {
|
||||||
@ -117,12 +127,27 @@ export default function EssayPage() {
|
|||||||
if (analysisError) throw analysisError;
|
if (analysisError) throw analysisError;
|
||||||
|
|
||||||
// Redireciona para a página de análise
|
// Redireciona para a página de análise
|
||||||
router.push(`/student-dashboard/essays/${essay.id}/analysis`);
|
navigate(`/aluno/redacoes/${essay.id}/analise`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Erro ao enviar para análise:', 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 <div>Carregando...</div>;
|
if (loading) return <div>Carregando...</div>;
|
||||||
if (!essay) return <div>Redação não encontrada</div>;
|
if (!essay) return <div>Redação não encontrada</div>;
|
||||||
|
|
||||||
@ -134,7 +159,15 @@ export default function EssayPage() {
|
|||||||
<div className="container mx-auto p-6">
|
<div className="container mx-auto p-6">
|
||||||
<div className="flex items-center justify-between mb-6">
|
<div className="flex items-center justify-between mb-6">
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
<Button variant="ghost" onClick={() => router.push('/student-dashboard/essays')}>
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
onClick={() => navigate('/aluno/redacoes')}
|
||||||
|
trackingId="essay-back-to-list-button"
|
||||||
|
trackingProperties={{
|
||||||
|
action: 'back_to_essays_list',
|
||||||
|
page: 'essay_editor'
|
||||||
|
}}
|
||||||
|
>
|
||||||
<ArrowLeft className="mr-2 h-4 w-4" />
|
<ArrowLeft className="mr-2 h-4 w-4" />
|
||||||
Voltar
|
Voltar
|
||||||
</Button>
|
</Button>
|
||||||
@ -150,26 +183,58 @@ export default function EssayPage() {
|
|||||||
<span>•</span>
|
<span>•</span>
|
||||||
<span>{essay.essay_genre.title}</span>
|
<span>{essay.essay_genre.title}</span>
|
||||||
<span>•</span>
|
<span>•</span>
|
||||||
<Badge variant={essay.status === 'draft' ? 'secondary' : 'primary'}>
|
<Badge variant={essay.status === 'draft' ? 'secondary' : 'default'}>
|
||||||
{essay.status === 'draft' ? 'Rascunho' : 'Enviada'}
|
{essay.status === 'draft' ? 'Rascunho' : 'Enviada'}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Button variant="outline" onClick={saveEssay} disabled={saving}>
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
onClick={saveEssay}
|
||||||
|
disabled={saving}
|
||||||
|
trackingId="essay-save-button"
|
||||||
|
trackingProperties={{
|
||||||
|
action: 'save_essay',
|
||||||
|
page: 'essay_editor',
|
||||||
|
status: essay.status
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Save className="mr-2 h-4 w-4" />
|
<Save className="mr-2 h-4 w-4" />
|
||||||
{saving ? 'Salvando...' : 'Salvar'}
|
{saving ? 'Salvando...' : 'Salvar'}
|
||||||
</Button>
|
</Button>
|
||||||
{essay.status === 'draft' && (
|
{essay.status === 'draft' && (
|
||||||
<Button
|
<>
|
||||||
onClick={submitForAnalysis}
|
<Button
|
||||||
disabled={!isWithinWordLimit}
|
onClick={submitForAnalysis}
|
||||||
title={!isWithinWordLimit ? 'Número de palavras fora do limite' : ''}
|
disabled={!isWithinWordLimit}
|
||||||
>
|
title={!isWithinWordLimit ? 'Número de palavras fora do limite' : ''}
|
||||||
<Send className="mr-2 h-4 w-4" />
|
trackingId="essay-submit-analysis-button"
|
||||||
Enviar para análise
|
trackingProperties={{
|
||||||
</Button>
|
action: 'submit_for_analysis',
|
||||||
|
page: 'essay_editor',
|
||||||
|
word_count: wordCount,
|
||||||
|
within_limit: isWithinWordLimit
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Send className="mr-2 h-4 w-4" />
|
||||||
|
Enviar para análise
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="destructive"
|
||||||
|
onClick={() => setShowDeleteDialog(true)}
|
||||||
|
trackingId="essay-delete-button"
|
||||||
|
trackingProperties={{
|
||||||
|
action: 'delete_essay',
|
||||||
|
page: 'essay_editor',
|
||||||
|
status: essay.status
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Trash2 className="mr-2 h-4 w-4" />
|
||||||
|
Deletar
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -209,6 +274,23 @@ export default function EssayPage() {
|
|||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<AlertDialog open={showDeleteDialog} onOpenChange={setShowDeleteDialog}>
|
||||||
|
<AlertDialogContent>
|
||||||
|
<AlertDialogHeader>
|
||||||
|
<AlertDialogTitle>Você tem certeza?</AlertDialogTitle>
|
||||||
|
<AlertDialogDescription>
|
||||||
|
Esta ação não pode ser desfeita. Isso excluirá permanentemente sua redação.
|
||||||
|
</AlertDialogDescription>
|
||||||
|
</AlertDialogHeader>
|
||||||
|
<AlertDialogFooter>
|
||||||
|
<AlertDialogCancel>Cancelar</AlertDialogCancel>
|
||||||
|
<AlertDialogAction onClick={deleteEssay} className="bg-red-600 hover:bg-red-700">
|
||||||
|
Deletar
|
||||||
|
</AlertDialogAction>
|
||||||
|
</AlertDialogFooter>
|
||||||
|
</AlertDialogContent>
|
||||||
|
</AlertDialog>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1,5 +1,5 @@
|
|||||||
import { useState } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { useRouter } from 'next/router';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { supabase } from '@/lib/supabase';
|
import { supabase } from '@/lib/supabase';
|
||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
@ -24,15 +24,18 @@ interface EssayGenre {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function NewEssayPage() {
|
export function NewEssay() {
|
||||||
const router = useRouter();
|
const navigate = useNavigate();
|
||||||
const { supabase } = supabase();
|
|
||||||
const [step, setStep] = useState<'type' | 'genre'>('type');
|
const [step, setStep] = useState<'type' | 'genre'>('type');
|
||||||
const [selectedType, setSelectedType] = useState<EssayType | null>(null);
|
const [selectedType, setSelectedType] = useState<EssayType | null>(null);
|
||||||
const [types, setTypes] = useState<EssayType[]>([]);
|
const [types, setTypes] = useState<EssayType[]>([]);
|
||||||
const [genres, setGenres] = useState<EssayGenre[]>([]);
|
const [genres, setGenres] = useState<EssayGenre[]>([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
loadTypes();
|
||||||
|
}, []);
|
||||||
|
|
||||||
// Carregar tipos textuais
|
// Carregar tipos textuais
|
||||||
async function loadTypes() {
|
async function loadTypes() {
|
||||||
try {
|
try {
|
||||||
@ -69,9 +72,13 @@ export default function NewEssayPage() {
|
|||||||
// Criar nova redação
|
// Criar nova redação
|
||||||
async function createEssay(genreId: string) {
|
async function createEssay(genreId: string) {
|
||||||
try {
|
try {
|
||||||
|
const { data: { user } } = await supabase.auth.getUser();
|
||||||
|
if (!user) throw new Error('Usuário não autenticado');
|
||||||
|
|
||||||
const { data, error } = await supabase
|
const { data, error } = await supabase
|
||||||
.from('student_essays')
|
.from('student_essays')
|
||||||
.insert({
|
.insert({
|
||||||
|
student_id: user.id,
|
||||||
type_id: selectedType!.id,
|
type_id: selectedType!.id,
|
||||||
genre_id: genreId,
|
genre_id: genreId,
|
||||||
status: 'draft',
|
status: 'draft',
|
||||||
@ -82,7 +89,7 @@ export default function NewEssayPage() {
|
|||||||
.single();
|
.single();
|
||||||
|
|
||||||
if (error) throw error;
|
if (error) throw error;
|
||||||
router.push(`/student-dashboard/essays/${data.id}`);
|
navigate(`/aluno/redacoes/${data.id}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Erro ao criar redação:', error);
|
console.error('Erro ao criar redação:', error);
|
||||||
}
|
}
|
||||||
@ -156,7 +163,16 @@ export default function NewEssayPage() {
|
|||||||
<div className="container mx-auto p-6">
|
<div className="container mx-auto p-6">
|
||||||
<div className="flex items-center gap-4 mb-6">
|
<div className="flex items-center gap-4 mb-6">
|
||||||
{step === 'genre' && (
|
{step === 'genre' && (
|
||||||
<Button variant="ghost" onClick={() => setStep('type')}>
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
onClick={() => setStep('type')}
|
||||||
|
trackingId="essay-new-back-button"
|
||||||
|
trackingProperties={{
|
||||||
|
action: 'back_to_type_selection',
|
||||||
|
current_step: 'genre',
|
||||||
|
page: 'new_essay'
|
||||||
|
}}
|
||||||
|
>
|
||||||
<ArrowLeft className="mr-2 h-4 w-4" />
|
<ArrowLeft className="mr-2 h-4 w-4" />
|
||||||
Voltar
|
Voltar
|
||||||
</Button>
|
</Button>
|
||||||
@ -1,9 +1,9 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { supabase } from '@/lib/supabase';
|
import { supabase } from '@/lib/supabase';
|
||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { PlusCircle } from 'lucide-react';
|
import { PlusCircle } from 'lucide-react';
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import { Badge } from '@/components/ui/badge';
|
import { Badge } from '@/components/ui/badge';
|
||||||
|
|
||||||
interface Essay {
|
interface Essay {
|
||||||
@ -21,9 +21,8 @@ interface Essay {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function EssaysPage() {
|
export function EssaysPage() {
|
||||||
const router = useRouter();
|
const navigate = useNavigate();
|
||||||
const { supabase } = supabase();
|
|
||||||
const [essays, setEssays] = useState<Essay[]>([]);
|
const [essays, setEssays] = useState<Essay[]>([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
@ -53,20 +52,27 @@ export default function EssaysPage() {
|
|||||||
|
|
||||||
function getStatusBadge(status: Essay['status']) {
|
function getStatusBadge(status: Essay['status']) {
|
||||||
const statusMap = {
|
const statusMap = {
|
||||||
draft: { label: 'Rascunho', variant: 'secondary' },
|
draft: { label: 'Rascunho', variant: 'secondary' as const },
|
||||||
submitted: { label: 'Enviada', variant: 'primary' },
|
submitted: { label: 'Enviada', variant: 'default' as const },
|
||||||
analyzed: { label: 'Analisada', variant: 'success' }
|
analyzed: { label: 'Analisada', variant: 'success' as const }
|
||||||
};
|
};
|
||||||
|
|
||||||
const { label, variant } = statusMap[status];
|
const { label, variant } = statusMap[status];
|
||||||
return <Badge variant={variant as any}>{label}</Badge>;
|
return <Badge variant={variant}>{label}</Badge>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="container mx-auto p-6">
|
<div className="container mx-auto p-6">
|
||||||
<div className="flex justify-between items-center mb-6">
|
<div className="flex justify-between items-center mb-6">
|
||||||
<h1 className="text-3xl font-bold">Minhas Redações</h1>
|
<h1 className="text-3xl font-bold">Minhas Redações</h1>
|
||||||
<Button onClick={() => router.push('/student-dashboard/essays/new')}>
|
<Button
|
||||||
|
onClick={() => navigate('/aluno/redacoes/nova')}
|
||||||
|
trackingId="essay-new-create-button"
|
||||||
|
trackingProperties={{
|
||||||
|
action: 'create_new_essay',
|
||||||
|
page: 'essays_list'
|
||||||
|
}}
|
||||||
|
>
|
||||||
<PlusCircle className="mr-2 h-4 w-4" />
|
<PlusCircle className="mr-2 h-4 w-4" />
|
||||||
Nova Redação
|
Nova Redação
|
||||||
</Button>
|
</Button>
|
||||||
@ -78,7 +84,15 @@ export default function EssaysPage() {
|
|||||||
<Card>
|
<Card>
|
||||||
<CardContent className="flex flex-col items-center justify-center p-6">
|
<CardContent className="flex flex-col items-center justify-center p-6">
|
||||||
<p className="text-muted-foreground mb-4">Você ainda não tem nenhuma redação</p>
|
<p className="text-muted-foreground mb-4">Você ainda não tem nenhuma redação</p>
|
||||||
<Button onClick={() => router.push('/student-dashboard/essays/new')}>
|
<Button
|
||||||
|
onClick={() => navigate('/aluno/redacoes/nova')}
|
||||||
|
trackingId="essay-empty-create-button"
|
||||||
|
trackingProperties={{
|
||||||
|
action: 'create_first_essay',
|
||||||
|
page: 'essays_list',
|
||||||
|
context: 'empty_state'
|
||||||
|
}}
|
||||||
|
>
|
||||||
Criar Primeira Redação
|
Criar Primeira Redação
|
||||||
</Button>
|
</Button>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
@ -87,7 +101,7 @@ export default function EssaysPage() {
|
|||||||
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
||||||
{essays.map((essay) => (
|
{essays.map((essay) => (
|
||||||
<Card key={essay.id} className="cursor-pointer hover:shadow-lg transition-shadow"
|
<Card key={essay.id} className="cursor-pointer hover:shadow-lg transition-shadow"
|
||||||
onClick={() => router.push(`/student-dashboard/essays/${essay.id}`)}>
|
onClick={() => navigate(`/aluno/redacoes/${essay.id}`)}>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<div className="flex justify-between items-start">
|
<div className="flex justify-between items-start">
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@ -34,9 +34,9 @@ import { TextSalesLetter } from './pages/landing/TextSalesLetter';
|
|||||||
import { PhonicsPage } from "./pages/student-dashboard/PhonicsPage";
|
import { PhonicsPage } from "./pages/student-dashboard/PhonicsPage";
|
||||||
import { PhonicsProgressPage } from "./pages/student-dashboard/PhonicsProgressPage";
|
import { PhonicsProgressPage } from "./pages/student-dashboard/PhonicsProgressPage";
|
||||||
import { EssaysPage } from './pages/student-dashboard/essays';
|
import { EssaysPage } from './pages/student-dashboard/essays';
|
||||||
import { NewEssayPage } from './pages/student-dashboard/essays/new';
|
import { NewEssay } from './pages/student-dashboard/essays/NewEssay';
|
||||||
import { EssayPage } from './pages/student-dashboard/essays/[id]';
|
import { EssayPage } from './pages/student-dashboard/essays/EssayPage';
|
||||||
import { EssayAnalysisPage } from './pages/student-dashboard/essays/[id]/analysis';
|
import { EssayAnalysis } from './pages/student-dashboard/essays/EssayAnalysis';
|
||||||
|
|
||||||
function RootLayout({ children }: { children: React.ReactNode }) {
|
function RootLayout({ children }: { children: React.ReactNode }) {
|
||||||
return (
|
return (
|
||||||
@ -233,7 +233,7 @@ export const router = createBrowserRouter([
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'nova',
|
path: 'nova',
|
||||||
element: <NewEssayPage />,
|
element: <NewEssay />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: ':id',
|
path: ':id',
|
||||||
@ -241,7 +241,7 @@ export const router = createBrowserRouter([
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: ':id/analise',
|
path: ':id/analise',
|
||||||
element: <EssayAnalysisPage />,
|
element: <EssayAnalysis />,
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user