From 6be80bbd810e58521e8a7aa4bf6628abd97361fe Mon Sep 17 00:00:00 2001 From: hamid zarghami Date: Tue, 27 May 2025 11:26:16 +0330 Subject: [PATCH] feat: add sent messages list page with filters and table --- components.json | 21 +++ package.json | 5 + pnpm-lock.yaml | 246 +++++++++++++++++++++++++++++++++ src/App.css | 42 ------ src/components/Input.tsx | 2 +- src/components/Table.tsx | 37 +++-- src/components/Td.tsx | 2 +- src/components/ui/checkbox.tsx | 30 ++++ src/index.css | 119 ++++++++++++++++ src/langs/fa.json | 7 + src/lib/utils.ts | 6 + src/pages/sent/List.tsx | 97 +++++++++++++ src/pages/setting/Setting.tsx | 4 +- src/router/AppRouter.tsx | 2 + 14 files changed, 566 insertions(+), 54 deletions(-) create mode 100644 components.json delete mode 100644 src/App.css create mode 100644 src/components/ui/checkbox.tsx create mode 100644 src/lib/utils.ts create mode 100644 src/pages/sent/List.tsx diff --git a/components.json b/components.json new file mode 100644 index 0000000..73afbdb --- /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" +} \ No newline at end of file diff --git a/package.json b/package.json index 9f66338..9ba4eef 100644 --- a/package.json +++ b/package.json @@ -10,9 +10,13 @@ "preview": "vite preview" }, "dependencies": { + "@radix-ui/react-checkbox": "^1.3.2", "@tailwindcss/vite": "^4.1.4", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", "i18next": "^25.0.1", "iconsax-react": "^0.0.8", + "lucide-react": "^0.511.0", "react": "^19.0.0", "react-date-object": "^2.1.9", "react-dom": "^19.0.0", @@ -36,6 +40,7 @@ "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-refresh": "^0.4.19", "globals": "^16.0.0", + "tw-animate-css": "^1.3.0", "typescript": "~5.7.2", "typescript-eslint": "^8.26.1", "vite": "^6.3.1" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d93b448..1c861df 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,15 +8,27 @@ importers: .: dependencies: + '@radix-ui/react-checkbox': + specifier: ^1.3.2 + version: 1.3.2(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@tailwindcss/vite': specifier: ^4.1.4 version: 4.1.4(vite@6.3.3(jiti@2.4.2)(lightningcss@1.29.2)) + class-variance-authority: + specifier: ^0.7.1 + version: 0.7.1 + clsx: + specifier: ^2.1.1 + version: 2.1.1 i18next: specifier: ^25.0.1 version: 25.0.1(typescript@5.7.3) iconsax-react: specifier: ^0.0.8 version: 0.0.8(react@19.1.0) + lucide-react: + specifier: ^0.511.0 + version: 0.511.0(react@19.1.0) react: specifier: ^19.0.0 version: 19.1.0 @@ -81,6 +93,9 @@ importers: globals: specifier: ^16.0.0 version: 16.0.0 + tw-animate-css: + specifier: ^1.3.0 + version: 1.3.0 typescript: specifier: ~5.7.2 version: 5.7.3 @@ -418,6 +433,120 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@radix-ui/primitive@1.1.2': + resolution: {integrity: sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==} + + '@radix-ui/react-checkbox@1.3.2': + resolution: {integrity: sha512-yd+dI56KZqawxKZrJ31eENUwqc1QSqg4OZ15rybGjF2ZNwMO+wCyHzAVLRp9qoYJf7kYy0YpZ2b0JCzJ42HZpA==} + 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 + + '@radix-ui/react-compose-refs@1.1.2': + resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-context@1.1.2': + resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-presence@1.1.4': + resolution: {integrity: sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==} + 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 + + '@radix-ui/react-primitive@2.1.3': + resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==} + 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 + + '@radix-ui/react-slot@1.2.3': + resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-controllable-state@1.2.2': + resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-effect-event@0.0.2': + resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-layout-effect@1.1.1': + resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-previous@1.1.1': + resolution: {integrity: sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-size@1.1.1': + resolution: {integrity: sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@rollup/rollup-android-arm-eabi@4.40.0': resolution: {integrity: sha512-+Fbls/diZ0RDerhE8kyC6hjADCXA1K4yVNlH0EYfd2XjyH0UGgzaQ8MlT0pCXAThfxv3QUAczHaL+qSv1E4/Cg==} cpu: [arm] @@ -736,6 +865,13 @@ packages: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} + class-variance-authority@0.7.1: + resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -1092,6 +1228,11 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + lucide-react@0.511.0: + resolution: {integrity: sha512-VK5a2ydJ7xm8GvBeKLS9mu1pVK6ucef9780JVUjw6bAjJL/QXnd4Y0p7SPeOUMC27YhzNCZvm5d/QX0Tp3rc0w==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -1348,6 +1489,9 @@ packages: turbo-stream@2.4.0: resolution: {integrity: sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g==} + tw-animate-css@1.3.0: + resolution: {integrity: sha512-jrJ0XenzS9KVuDThJDvnhalbl4IYiMQ/XvpA0a2FL8KmlK+6CSMviO7ROY/I7z1NnUs5NnDhlM6fXmF40xPxzw==} + type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} @@ -1741,6 +1885,96 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.19.1 + '@radix-ui/primitive@1.1.2': {} + + '@radix-ui/react-checkbox@1.3.2(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.2)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.2)(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.2)(react@19.1.0) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.2)(react@19.1.0) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.1.2)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.2 + '@types/react-dom': 19.1.2(@types/react@19.1.2) + + '@radix-ui/react-compose-refs@1.1.2(@types/react@19.1.2)(react@19.1.0)': + dependencies: + react: 19.1.0 + optionalDependencies: + '@types/react': 19.1.2 + + '@radix-ui/react-context@1.1.2(@types/react@19.1.2)(react@19.1.0)': + dependencies: + react: 19.1.0 + optionalDependencies: + '@types/react': 19.1.2 + + '@radix-ui/react-presence@1.1.4(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.2)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.2)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.2 + '@types/react-dom': 19.1.2(@types/react@19.1.2) + + '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.2)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.2 + '@types/react-dom': 19.1.2(@types/react@19.1.2) + + '@radix-ui/react-slot@1.2.3(@types/react@19.1.2)(react@19.1.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.2)(react@19.1.0) + react: 19.1.0 + optionalDependencies: + '@types/react': 19.1.2 + + '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.1.2)(react@19.1.0)': + dependencies: + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.1.2)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.2)(react@19.1.0) + react: 19.1.0 + optionalDependencies: + '@types/react': 19.1.2 + + '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.1.2)(react@19.1.0)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.2)(react@19.1.0) + react: 19.1.0 + optionalDependencies: + '@types/react': 19.1.2 + + '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.1.2)(react@19.1.0)': + dependencies: + react: 19.1.0 + optionalDependencies: + '@types/react': 19.1.2 + + '@radix-ui/react-use-previous@1.1.1(@types/react@19.1.2)(react@19.1.0)': + dependencies: + react: 19.1.0 + optionalDependencies: + '@types/react': 19.1.2 + + '@radix-ui/react-use-size@1.1.1(@types/react@19.1.2)(react@19.1.0)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.2)(react@19.1.0) + react: 19.1.0 + optionalDependencies: + '@types/react': 19.1.2 + '@rollup/rollup-android-arm-eabi@4.40.0': optional: true @@ -2037,6 +2271,12 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 + class-variance-authority@0.7.1: + dependencies: + clsx: 2.1.1 + + clsx@2.1.1: {} + color-convert@2.0.1: dependencies: color-name: 1.1.4 @@ -2370,6 +2610,10 @@ snapshots: dependencies: yallist: 3.1.1 + lucide-react@0.511.0(react@19.1.0): + dependencies: + react: 19.1.0 + merge2@1.4.1: {} micromatch@4.0.8: @@ -2588,6 +2832,8 @@ snapshots: turbo-stream@2.4.0: {} + tw-animate-css@1.3.0: {} + type-check@0.4.0: dependencies: prelude-ls: 1.2.1 diff --git a/src/App.css b/src/App.css deleted file mode 100644 index b9d355d..0000000 --- a/src/App.css +++ /dev/null @@ -1,42 +0,0 @@ -#root { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; -} - -.logo { - height: 6em; - padding: 1.5em; - will-change: filter; - transition: filter 300ms; -} -.logo:hover { - filter: drop-shadow(0 0 2em #646cffaa); -} -.logo.react:hover { - filter: drop-shadow(0 0 2em #61dafbaa); -} - -@keyframes logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} - -@media (prefers-reduced-motion: no-preference) { - a:nth-of-type(2) .logo { - animation: logo-spin infinite 20s linear; - } -} - -.card { - padding: 2em; -} - -.read-the-docs { - color: #888; -} diff --git a/src/components/Input.tsx b/src/components/Input.tsx index 9a3dee0..1042666 100644 --- a/src/components/Input.tsx +++ b/src/components/Input.tsx @@ -101,7 +101,7 @@ const Input: FC = (props: Props) => { { props.variant === 'search' && - + } { diff --git a/src/components/Table.tsx b/src/components/Table.tsx index 98f3451..d0d9738 100644 --- a/src/components/Table.tsx +++ b/src/components/Table.tsx @@ -2,6 +2,7 @@ import React, { Fragment, useState } from 'react'; import DefaultTableSkeleton from './DefaultTableSkeleton'; import Td from './Td'; import { TableProps, RowDataType, ColumnType } from './types/TableTypes'; +import { Checkbox } from './ui/checkbox'; const Table = ({ columns, @@ -41,6 +42,18 @@ const Table = ({ setSelectedRows([...selectedRows, item]); } } + + if (onRowClick) { + onRowClick(item.id, item); + } + }; + + const handleSelectAll = () => { + if (selectedRows.length === data.length) { + setSelectedRows([]); + } else { + setSelectedRows([...data]); + } }; const renderTableBody = () => { @@ -61,16 +74,24 @@ const Table = ({ ); } - return data.map((item, rowIndex) => ( onRowClick && onRowClick(item.id, item)} + onClick={() => handleRowClick(item)} > {selectable && ( - - handleRowClick(item)} /> + + { + if (checked) { + setSelectedRows([...selectedRows, item]); + } else { + setSelectedRows(selectedRows.filter((row) => row !== item)); + } + }} + /> )} {columns.map((col) => ( @@ -91,11 +112,11 @@ const Table = ({ {selectable && ( { - e.stopPropagation(); - }} > - { }} /> + 0 && selectedRows.length === data.length} + onCheckedChange={handleSelectAll} + /> )} {columns.map((col) => ( diff --git a/src/components/Td.tsx b/src/components/Td.tsx index 1c90952..92c1f05 100644 --- a/src/components/Td.tsx +++ b/src/components/Td.tsx @@ -1,7 +1,7 @@ import { FC, ReactNode } from 'react' interface Props { - text: string, + text: string | number, children?: ReactNode, dir?: string, } diff --git a/src/components/ui/checkbox.tsx b/src/components/ui/checkbox.tsx new file mode 100644 index 0000000..defeb01 --- /dev/null +++ b/src/components/ui/checkbox.tsx @@ -0,0 +1,30 @@ +import * as React from "react" +import * as CheckboxPrimitive from "@radix-ui/react-checkbox" +import { CheckIcon } from "lucide-react" + +import { cn } from "@/lib/utils" + +function Checkbox({ + className, + ...props +}: React.ComponentProps) { + return ( + + + + + + ) +} + +export { Checkbox } diff --git a/src/index.css b/src/index.css index 20ad86d..7fd52a2 100644 --- a/src/index.css +++ b/src/index.css @@ -1,4 +1,7 @@ @import "tailwindcss"; +@import "tw-animate-css"; + +@custom-variant dark (&:is(.dark *)); html { direction: rtl; @@ -79,3 +82,119 @@ td { .rmdp-container { width: 100%; } + +@theme inline { + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-card: var(--card); + --color-card-foreground: var(--card-foreground); + --color-popover: var(--popover); + --color-popover-foreground: var(--popover-foreground); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-foreground); + --color-destructive: var(--destructive); + --color-border: var(--border); + --color-input: var(--input); + --color-ring: var(--ring); + --color-chart-1: var(--chart-1); + --color-chart-2: var(--chart-2); + --color-chart-3: var(--chart-3); + --color-chart-4: var(--chart-4); + --color-chart-5: var(--chart-5); + --color-sidebar: var(--sidebar); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-ring: var(--sidebar-ring); +} + +:root { + --radius: 0.625rem; + --background: oklch(1 0 0); + --foreground: oklch(0.145 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.145 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.145 0 0); + --primary: oklch(0.205 0 0); + --primary-foreground: oklch(0.985 0 0); + --secondary: oklch(0.97 0 0); + --secondary-foreground: oklch(0.205 0 0); + --muted: oklch(0.97 0 0); + --muted-foreground: oklch(0.556 0 0); + --accent: oklch(0.97 0 0); + --accent-foreground: oklch(0.205 0 0); + --destructive: oklch(0.577 0.245 27.325); + --border: oklch(0.922 0 0); + --input: oklch(0.922 0 0); + --ring: oklch(0.708 0 0); + --chart-1: oklch(0.646 0.222 41.116); + --chart-2: oklch(0.6 0.118 184.704); + --chart-3: oklch(0.398 0.07 227.392); + --chart-4: oklch(0.828 0.189 84.429); + --chart-5: oklch(0.769 0.188 70.08); + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.145 0 0); + --sidebar-primary: oklch(0.205 0 0); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.97 0 0); + --sidebar-accent-foreground: oklch(0.205 0 0); + --sidebar-border: oklch(0.922 0 0); + --sidebar-ring: oklch(0.708 0 0); +} + +.dark { + --background: oklch(0.145 0 0); + --foreground: oklch(0.985 0 0); + --card: oklch(0.205 0 0); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.205 0 0); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.922 0 0); + --primary-foreground: oklch(0.205 0 0); + --secondary: oklch(0.269 0 0); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.269 0 0); + --muted-foreground: oklch(0.708 0 0); + --accent: oklch(0.269 0 0); + --accent-foreground: oklch(0.985 0 0); + --destructive: oklch(0.704 0.191 22.216); + --border: oklch(1 0 0 / 10%); + --input: oklch(1 0 0 / 15%); + --ring: oklch(0.556 0 0); + --chart-1: oklch(0.488 0.243 264.376); + --chart-2: oklch(0.696 0.17 162.48); + --chart-3: oklch(0.769 0.188 70.08); + --chart-4: oklch(0.627 0.265 303.9); + --chart-5: oklch(0.645 0.246 16.439); + --sidebar: oklch(0.205 0 0); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.488 0.243 264.376); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.269 0 0); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(1 0 0 / 10%); + --sidebar-ring: oklch(0.556 0 0); +} + +@layer base { + * { + @apply border-border outline-ring/50; + } + body { + @apply bg-background text-foreground; + } +} diff --git a/src/langs/fa.json b/src/langs/fa.json index adc1e29..bf4290c 100644 --- a/src/langs/fa.json +++ b/src/langs/fa.json @@ -24,6 +24,13 @@ "all": "همه", "search": "جستجو" }, + "sent": { + "title": "ارسال شده ها", + "from_date": "از تاریخ", + "to_date": "تا تاریخ", + "all": "همه", + "search": "جستجو" + }, "setting": { "title": "تنظیمات", "personality": "شخصی سازی", diff --git a/src/lib/utils.ts b/src/lib/utils.ts new file mode 100644 index 0000000..bd0c391 --- /dev/null +++ b/src/lib/utils.ts @@ -0,0 +1,6 @@ +import { clsx, type ClassValue } from "clsx" +import { twMerge } from "tailwind-merge" + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)) +} diff --git a/src/pages/sent/List.tsx b/src/pages/sent/List.tsx new file mode 100644 index 0000000..ddb5aeb --- /dev/null +++ b/src/pages/sent/List.tsx @@ -0,0 +1,97 @@ +import Filters, { FilterValues } from '../../components/Filters'; +import { FC, useState } from 'react' +import { useTranslation } from 'react-i18next' +import Table from '../../components/Table'; +import { ColumnType } from '../../components/types/TableTypes'; +import { InfoCircle, More, Refresh2 } from 'iconsax-react'; + +interface Message extends Record { + id: string; + title: string; + sender: string; + date: string; + read: boolean; +} + +const List: FC = () => { + const { t } = useTranslation(); + const [isLoading] = useState(false); + + const messageData: Message[] = [ + { id: '1', title: 'اولین پیام', sender: 'علی محمدی', date: '1402/05/12', read: true }, + { id: '2', title: 'دومین پیام', sender: 'زهرا احمدی', date: '1402/05/13', read: true }, + { id: '3', title: 'جلسه هفتگی', sender: 'مدیریت', date: '1402/05/14', read: true }, + ]; + + const columns: ColumnType[] = [ + { + key: 'title', + title: 'عنوان پیام', + render: (item) => ( +
+ {!item.read &&
} + {item.title} +
+ ) + }, + { key: 'sender', title: 'فرستنده' }, + { key: 'date', title: 'تاریخ', align: 'center' }, + ]; + + const tableActions = ( +
+ + +
+ ); + + const handleFilterChange = (newFilters: FilterValues) => { + console.log('Applied filters:', newFilters); + }; + + return ( +
+

{t('sent.title')}

+ + + + + columns={columns} + data={messageData} + isLoading={isLoading} + showHeader={false} + actions={tableActions} + onRowClick={(id) => console.log(`کلیک روی پیام با شناسه ${id}`)} + noDataMessage={ +
+ +
هیچ پیامی یافت نشد
+
+ } + selectable={true} + /> + +
+ ) +} + +export default List diff --git a/src/pages/setting/Setting.tsx b/src/pages/setting/Setting.tsx index 8addfa0..2aaf41c 100644 --- a/src/pages/setting/Setting.tsx +++ b/src/pages/setting/Setting.tsx @@ -17,7 +17,7 @@ const Setting: FC = () => { items={[ { icon: , - label: t('setting.personality'), + label: t('setting.address'), value: SettingTabEnum.SETTING_MAIL_SERVER }, { @@ -32,7 +32,7 @@ const Setting: FC = () => { }, { icon: , - label: t('setting.address'), + label: t('setting.personality'), value: SettingTabEnum.SETTING_PERSONALITY } ]} diff --git a/src/router/AppRouter.tsx b/src/router/AppRouter.tsx index c61934e..2e8059c 100644 --- a/src/router/AppRouter.tsx +++ b/src/router/AppRouter.tsx @@ -4,12 +4,14 @@ import { Routes, Route } from 'react-router-dom' import Home from '@/pages/home/Home' import ReceivedList from '@/pages/received/List' import Setting from '@/pages/setting/Setting' +import SentList from '@/pages/sent/List' const AppRouter: FC = () => { return ( } /> } /> } /> + } /> ) }