diff --git a/package.json b/package.json index 5b8b69e..e133dfe 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "tailwindcss": "^4.3.1", "typescript": "^5.6.2", "typescript-eslint": "^8.61.1", - "vite": "^8.0.12" + "vite": "^8.0.12", + "vite-plugin-svgr": "^5.2.0" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3f41338..74b5131 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -81,6 +81,9 @@ importers: vite: specifier: ^8.0.12 version: 8.0.16(@types/node@20.19.43)(jiti@2.7.0) + vite-plugin-svgr: + specifier: ^5.2.0 + version: 5.2.0(typescript@5.9.3)(vite@8.0.16(@types/node@20.19.43)(jiti@2.7.0)) packages: @@ -349,6 +352,83 @@ packages: '@rolldown/pluginutils@1.0.1': resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + '@rollup/pluginutils@5.4.0': + resolution: {integrity: sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@svgr/babel-plugin-add-jsx-attribute@8.0.0': + resolution: {integrity: sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-remove-jsx-attribute@8.0.0': + resolution: {integrity: sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0': + resolution: {integrity: sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0': + resolution: {integrity: sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-svg-dynamic-title@8.0.0': + resolution: {integrity: sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-svg-em-dimensions@8.0.0': + resolution: {integrity: sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-transform-react-native-svg@8.1.0': + resolution: {integrity: sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-transform-svg-component@8.0.0': + resolution: {integrity: sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==} + engines: {node: '>=12'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-preset@8.1.0': + resolution: {integrity: sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/core@8.1.0': + resolution: {integrity: sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==} + engines: {node: '>=14'} + + '@svgr/hast-util-to-babel-ast@8.0.0': + resolution: {integrity: sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==} + engines: {node: '>=14'} + + '@svgr/plugin-jsx@8.1.0': + resolution: {integrity: sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==} + engines: {node: '>=14'} + peerDependencies: + '@svgr/core': '*' + '@tailwindcss/node@4.3.1': resolution: {integrity: sha512-6NDaqRoAMSXD1mr/RXu0HBvNE9a2n5tHPsxu9XHLws8o4Twes5rBM2205SUUiJ9goAtadrN6xTGX0UDEwp/N4A==} @@ -569,6 +649,9 @@ packages: ajv@6.15.0: resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} @@ -604,6 +687,14 @@ packages: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + caniuse-lite@1.0.30001799: resolution: {integrity: sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==} @@ -618,6 +709,15 @@ packages: resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} engines: {node: '>=18'} + cosmiconfig@8.3.6: + resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -645,6 +745,9 @@ packages: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} + dot-case@3.0.4: + resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} + dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} @@ -656,6 +759,13 @@ packages: resolution: {integrity: sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==} engines: {node: '>=10.13.0'} + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + error-ex@1.3.4: + resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + es-define-property@1.0.1: resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} engines: {node: '>= 0.4'} @@ -729,6 +839,9 @@ packages: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} @@ -847,10 +960,17 @@ packages: resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} engines: {node: '>= 4'} + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -869,6 +989,10 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + js-yaml@4.3.0: + resolution: {integrity: sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==} + hasBin: true + jsesc@3.1.0: resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} engines: {node: '>=6'} @@ -877,6 +1001,9 @@ packages: json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} @@ -972,10 +1099,16 @@ packages: resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} engines: {node: '>= 12.0.0'} + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} + lower-case@2.0.2: + resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} @@ -1009,6 +1142,9 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + no-case@3.0.4: + resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + node-releases@2.0.47: resolution: {integrity: sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og==} engines: {node: '>=18'} @@ -1025,6 +1161,14 @@ packages: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -1033,6 +1177,10 @@ packages: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -1092,6 +1240,10 @@ packages: resolution: {integrity: sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==} engines: {node: '>=0.10.0'} + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + rolldown@1.0.3: resolution: {integrity: sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==} engines: {node: ^20.19.0 || >=22.12.0} @@ -1120,10 +1272,16 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} + snake-case@3.0.4: + resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + svg-parser@2.0.4: + resolution: {integrity: sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==} + tailwindcss@4.3.1: resolution: {integrity: sha512-hk+TB1m+K8CYNrP6rjQaq/Y+4Zylwpa87mLYBKCunwnnQ9p+fHb7kmSfGqyEJoxF/O6CDyABWVFEafNSYKll+Q==} @@ -1172,6 +1330,11 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + vite-plugin-svgr@5.2.0: + resolution: {integrity: sha512-qj2eAKF8C6PZWemVTvQA0xgQIcP1hHU6Buh7fl6BhvayWwnuxE+z417miKxeDvRWbDrupQ1oK99hfxElopJ3sQ==} + peerDependencies: + vite: '>=3.0.0' + vite@8.0.16: resolution: {integrity: sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -1493,6 +1656,82 @@ snapshots: '@rolldown/pluginutils@1.0.1': {} + '@rollup/pluginutils@5.4.0': + dependencies: + '@types/estree': 1.0.9 + estree-walker: 2.0.2 + picomatch: 4.0.4 + + '@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + + '@svgr/babel-plugin-remove-jsx-attribute@8.0.0(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + + '@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + + '@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + + '@svgr/babel-plugin-svg-dynamic-title@8.0.0(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + + '@svgr/babel-plugin-svg-em-dimensions@8.0.0(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + + '@svgr/babel-plugin-transform-react-native-svg@8.1.0(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + + '@svgr/babel-plugin-transform-svg-component@8.0.0(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + + '@svgr/babel-preset@8.1.0(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@svgr/babel-plugin-add-jsx-attribute': 8.0.0(@babel/core@7.29.7) + '@svgr/babel-plugin-remove-jsx-attribute': 8.0.0(@babel/core@7.29.7) + '@svgr/babel-plugin-remove-jsx-empty-expression': 8.0.0(@babel/core@7.29.7) + '@svgr/babel-plugin-replace-jsx-attribute-value': 8.0.0(@babel/core@7.29.7) + '@svgr/babel-plugin-svg-dynamic-title': 8.0.0(@babel/core@7.29.7) + '@svgr/babel-plugin-svg-em-dimensions': 8.0.0(@babel/core@7.29.7) + '@svgr/babel-plugin-transform-react-native-svg': 8.1.0(@babel/core@7.29.7) + '@svgr/babel-plugin-transform-svg-component': 8.0.0(@babel/core@7.29.7) + + '@svgr/core@8.1.0(typescript@5.9.3)': + dependencies: + '@babel/core': 7.29.7 + '@svgr/babel-preset': 8.1.0(@babel/core@7.29.7) + camelcase: 6.3.0 + cosmiconfig: 8.3.6(typescript@5.9.3) + snake-case: 3.0.4 + transitivePeerDependencies: + - supports-color + - typescript + + '@svgr/hast-util-to-babel-ast@8.0.0': + dependencies: + '@babel/types': 7.29.7 + entities: 4.5.0 + + '@svgr/plugin-jsx@8.1.0(@svgr/core@8.1.0(typescript@5.9.3))': + dependencies: + '@babel/core': 7.29.7 + '@svgr/babel-preset': 8.1.0(@babel/core@7.29.7) + '@svgr/core': 8.1.0(typescript@5.9.3) + '@svgr/hast-util-to-babel-ast': 8.0.0 + svg-parser: 2.0.4 + transitivePeerDependencies: + - supports-color + '@tailwindcss/node@4.3.1': dependencies: '@jridgewell/remapping': 2.3.5 @@ -1712,6 +1951,8 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 + argparse@2.0.1: {} + asynckit@0.4.0: {} autoprefixer@10.5.0(postcss@8.5.15): @@ -1754,6 +1995,10 @@ snapshots: es-errors: 1.3.0 function-bind: 1.1.2 + callsites@3.1.0: {} + + camelcase@6.3.0: {} + caniuse-lite@1.0.30001799: {} combined-stream@1.0.8: @@ -1764,6 +2009,15 @@ snapshots: cookie@1.1.1: {} + cosmiconfig@8.3.6(typescript@5.9.3): + dependencies: + import-fresh: 3.3.1 + js-yaml: 4.3.0 + parse-json: 5.2.0 + path-type: 4.0.0 + optionalDependencies: + typescript: 5.9.3 + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -1782,6 +2036,11 @@ snapshots: detect-libc@2.1.2: {} + dot-case@3.0.4: + dependencies: + no-case: 3.0.4 + tslib: 2.8.1 + dunder-proto@1.0.1: dependencies: call-bind-apply-helpers: 1.0.2 @@ -1795,6 +2054,12 @@ snapshots: graceful-fs: 4.2.11 tapable: 2.3.3 + entities@4.5.0: {} + + error-ex@1.3.4: + dependencies: + is-arrayish: 0.2.1 + es-define-property@1.0.1: {} es-errors@1.3.0: {} @@ -1893,6 +2158,8 @@ snapshots: estraverse@5.3.0: {} + estree-walker@2.0.2: {} + esutils@2.0.3: {} fast-deep-equal@3.1.3: {} @@ -1995,8 +2262,15 @@ snapshots: ignore@7.0.5: {} + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + imurmurhash@0.1.4: {} + is-arrayish@0.2.1: {} + is-extglob@2.1.1: {} is-glob@4.0.3: @@ -2009,10 +2283,16 @@ snapshots: js-tokens@4.0.0: {} + js-yaml@4.3.0: + dependencies: + argparse: 2.0.1 + jsesc@3.1.0: {} json-buffer@3.0.1: {} + json-parse-even-better-errors@2.3.1: {} + json-schema-traverse@0.4.1: {} json-stable-stringify-without-jsonify@1.0.1: {} @@ -2079,10 +2359,16 @@ snapshots: lightningcss-win32-arm64-msvc: 1.32.0 lightningcss-win32-x64-msvc: 1.32.0 + lines-and-columns@1.2.4: {} + locate-path@6.0.0: dependencies: p-locate: 5.0.0 + lower-case@2.0.2: + dependencies: + tslib: 2.8.1 + lru-cache@5.1.1: dependencies: yallist: 3.1.1 @@ -2109,6 +2395,11 @@ snapshots: natural-compare@1.4.0: {} + no-case@3.0.4: + dependencies: + lower-case: 2.0.2 + tslib: 2.8.1 + node-releases@2.0.47: {} optionator@0.9.4: @@ -2128,10 +2419,23 @@ snapshots: dependencies: p-limit: 3.1.0 + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.29.7 + error-ex: 1.3.4 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + path-exists@4.0.0: {} path-key@3.1.1: {} + path-type@4.0.0: {} + picocolors@1.1.1: {} picomatch@4.0.4: {} @@ -2178,6 +2482,8 @@ snapshots: react@19.2.7: {} + resolve-from@4.0.0: {} + rolldown@1.0.3: dependencies: '@oxc-project/types': 0.133.0 @@ -2213,8 +2519,15 @@ snapshots: shebang-regex@3.0.0: {} + snake-case@3.0.4: + dependencies: + dot-case: 3.0.4 + tslib: 2.8.1 + source-map-js@1.2.1: {} + svg-parser@2.0.4: {} + tailwindcss@4.3.1: {} tapable@2.3.3: {} @@ -2228,8 +2541,7 @@ snapshots: dependencies: typescript: 5.9.3 - tslib@2.8.1: - optional: true + tslib@2.8.1: {} type-check@0.4.0: dependencies: @@ -2260,6 +2572,17 @@ snapshots: dependencies: punycode: 2.3.1 + vite-plugin-svgr@5.2.0(typescript@5.9.3)(vite@8.0.16(@types/node@20.19.43)(jiti@2.7.0)): + dependencies: + '@rollup/pluginutils': 5.4.0 + '@svgr/core': 8.1.0(typescript@5.9.3) + '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.9.3)) + vite: 8.0.16(@types/node@20.19.43)(jiti@2.7.0) + transitivePeerDependencies: + - rollup + - supports-color + - typescript + vite@8.0.16(@types/node@20.19.43)(jiti@2.7.0): dependencies: lightningcss: 1.32.0 diff --git a/src/assets/calendar.svg b/src/assets/calendar.svg deleted file mode 100644 index ef1c748..0000000 --- a/src/assets/calendar.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/src/assets/arrow-right.svg b/src/assets/icons/arrow-right.svg similarity index 52% rename from src/assets/arrow-right.svg rename to src/assets/icons/arrow-right.svg index 2694710..42d747d 100644 --- a/src/assets/arrow-right.svg +++ b/src/assets/icons/arrow-right.svg @@ -1,3 +1,3 @@ - + diff --git a/src/assets/book.svg b/src/assets/icons/book.svg similarity index 53% rename from src/assets/book.svg rename to src/assets/icons/book.svg index a95cb42..b7272d9 100644 --- a/src/assets/book.svg +++ b/src/assets/icons/book.svg @@ -1,6 +1,6 @@ - - - - + + + + diff --git a/src/assets/icons/calendar.svg b/src/assets/icons/calendar.svg new file mode 100644 index 0000000..fdd352e --- /dev/null +++ b/src/assets/icons/calendar.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/assets/call.svg b/src/assets/icons/call.svg similarity index 92% rename from src/assets/call.svg rename to src/assets/icons/call.svg index 693c4bc..53f03e6 100644 --- a/src/assets/call.svg +++ b/src/assets/icons/call.svg @@ -1,3 +1,3 @@ - + diff --git a/src/assets/icons/check.svg b/src/assets/icons/check.svg new file mode 100644 index 0000000..3fb8d58 --- /dev/null +++ b/src/assets/icons/check.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icons/chevron-down.svg b/src/assets/icons/chevron-down.svg new file mode 100644 index 0000000..8515a64 --- /dev/null +++ b/src/assets/icons/chevron-down.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icons/chevron-left.svg b/src/assets/icons/chevron-left.svg new file mode 100644 index 0000000..666a49d --- /dev/null +++ b/src/assets/icons/chevron-left.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icons/chevron-right.svg b/src/assets/icons/chevron-right.svg new file mode 100644 index 0000000..7077bd9 --- /dev/null +++ b/src/assets/icons/chevron-right.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/edit-2.svg b/src/assets/icons/edit.svg similarity index 56% rename from src/assets/edit-2.svg rename to src/assets/icons/edit.svg index 5ee713b..d331b9d 100644 --- a/src/assets/edit-2.svg +++ b/src/assets/icons/edit.svg @@ -1,5 +1,5 @@ - - - + + + diff --git a/src/assets/fail_fallback.svg b/src/assets/icons/fail.svg similarity index 92% rename from src/assets/fail_fallback.svg rename to src/assets/icons/fail.svg index 1c902b2..831843c 100644 --- a/src/assets/fail_fallback.svg +++ b/src/assets/icons/fail.svg @@ -1,4 +1,4 @@ - + diff --git a/src/assets/info.svg b/src/assets/icons/info.svg similarity index 67% rename from src/assets/info.svg rename to src/assets/icons/info.svg index 1b66977..3ccb2eb 100644 --- a/src/assets/info.svg +++ b/src/assets/icons/info.svg @@ -1,3 +1,3 @@ - + diff --git a/src/assets/location.svg b/src/assets/icons/location.svg similarity index 80% rename from src/assets/location.svg rename to src/assets/icons/location.svg index 462a973..d4e558d 100644 --- a/src/assets/location.svg +++ b/src/assets/icons/location.svg @@ -1,4 +1,4 @@ - - + + diff --git a/src/assets/icons/minus.svg b/src/assets/icons/minus.svg new file mode 100644 index 0000000..d1f667b --- /dev/null +++ b/src/assets/icons/minus.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/plus.svg b/src/assets/icons/plus.svg similarity index 92% rename from src/assets/plus.svg rename to src/assets/icons/plus.svg index fc2de2c..6cedbc5 100644 --- a/src/assets/plus.svg +++ b/src/assets/icons/plus.svg @@ -1,3 +1,3 @@ - + diff --git a/src/assets/profile-circle.svg b/src/assets/icons/profile-circle.svg similarity index 72% rename from src/assets/profile-circle.svg rename to src/assets/icons/profile-circle.svg index 8f1f86a..eda34a3 100644 --- a/src/assets/profile-circle.svg +++ b/src/assets/icons/profile-circle.svg @@ -1,5 +1,5 @@ - - - + + + diff --git a/src/assets/search-normal.svg b/src/assets/icons/search.svg similarity index 51% rename from src/assets/search-normal.svg rename to src/assets/icons/search.svg index c0c4ec3..09e0d78 100644 --- a/src/assets/search-normal.svg +++ b/src/assets/icons/search.svg @@ -1,4 +1,4 @@ - - + + diff --git a/src/assets/success_fallback.svg b/src/assets/icons/success.svg similarity index 92% rename from src/assets/success_fallback.svg rename to src/assets/icons/success.svg index 4528e14..258c10e 100644 --- a/src/assets/success_fallback.svg +++ b/src/assets/icons/success.svg @@ -1,4 +1,4 @@ - + diff --git a/src/assets/minus.svg b/src/assets/minus.svg deleted file mode 100644 index e1d50eb..0000000 --- a/src/assets/minus.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/components/BookIcon.tsx b/src/components/BookIcon.tsx deleted file mode 100644 index 0115d55..0000000 --- a/src/components/BookIcon.tsx +++ /dev/null @@ -1,10 +0,0 @@ -export default function BookIcon() { - return ( - - - - - - - ) -} diff --git a/src/components/FailIcon.tsx b/src/components/FailIcon.tsx deleted file mode 100644 index 7305924..0000000 --- a/src/components/FailIcon.tsx +++ /dev/null @@ -1,18 +0,0 @@ -export default function FailIcon({ className = 'mx-auto h-28 w-28' }: { className?: string }) { - return ( - - - - - - ) -} diff --git a/src/components/Icon/Icon.tsx b/src/components/Icon/Icon.tsx new file mode 100644 index 0000000..8d577fd --- /dev/null +++ b/src/components/Icon/Icon.tsx @@ -0,0 +1,62 @@ +import { memo, type CSSProperties, type SVGProps } from 'react' +import { icons } from './icons' + +// `keyof typeof icons` derives the union straight from icons.ts's keys — add/remove/rename +// a key there and `name`'s autocomplete (and every existing call site, +// via a type error) updates with it. No second list of names to keep in sync. +export type IconName = keyof typeof icons + +export interface IconProps extends Omit, 'color'> { + /** Which icon to render. Autocompletes from icons.ts. */ + name: IconName + /** Sets width and height together. Ignored for either one passed explicitly. Defaults to 24. */ + size?: number | string + /** + * Recolors the icon by setting the CSS `color` property, which every icon in icons.ts + * paints with via `stroke="currentColor"`/`fill="currentColor"` — EXCEPT `success` and + * `fail`, whose fills are hardcoded brand colors (green/red) by design and won't move. + * + * Left undefined (the default) on purpose, rather than defaulting it to the literal + * string `'currentColor'`: an inline `style.color` always beats a `className` text-color + * utility (e.g. Tailwind's `text-gray-400`) regardless of specificity, since inline + * styles win outright. Only setting `style.color` when the caller actually passes + * `color` lets `className`-based coloring keep working untouched, while still landing on + * `currentColor` at rest — which is what "inherit the ambient text color" already means. + */ + color?: string +} + +function IconImpl({ name, size = 24, width, height, color, style, ...rest }: IconProps) { + const Svg = icons[name] + const mergedStyle: CSSProperties | undefined = color ? { color, ...style } : style + + // Decorative by default (aria-hidden) — the WAI-ARIA "img" pattern only applies once + // the icon carries its own meaning, signaled here by the caller passing `aria-label`. + // Spreading `...rest` last lets an explicit `role`/`aria-hidden`/etc. from the caller + // override these computed defaults. + const a11yProps = rest['aria-label'] ? { role: 'img' as const } : { 'aria-hidden': true as const } + + return ( + isn't part of the tab order in any current browser, but old + // IE/Edge would focus one — `focusable="false"` is the standard belt-and-suspenders + // fix. Every clickable icon in this app is already wrapped in a real
- +

رزرو با خطا مواجه شد. لطفا دوباره تلاش کنید.

- + {formatPersianNumber(count)} نفر @@ -221,7 +202,7 @@ export default function ReservationModal({ onClick={() => setCount((prev) => prev + 1)} className="flex h-6 w-6 items-center justify-center text-[#223D5D]" > - +
@@ -246,7 +227,7 @@ export default function ReservationModal({ />
- +

توجه داشته باشید که مسئولیت رزرو و حضور به‌موقع در کافه بر عهده کاربر است. diff --git a/src/components/SuccessIcon.tsx b/src/components/SuccessIcon.tsx deleted file mode 100644 index 98fecf7..0000000 --- a/src/components/SuccessIcon.tsx +++ /dev/null @@ -1,14 +0,0 @@ -export default function SuccessIcon({ className = 'mx-auto h-28 w-28' }: { className?: string }) { - return ( - - - - - ) -} diff --git a/src/pages/AddEvent.tsx b/src/pages/AddEvent.tsx index 43e9c9e..8392d69 100644 --- a/src/pages/AddEvent.tsx +++ b/src/pages/AddEvent.tsx @@ -5,6 +5,7 @@ import { createCafeSchedule, MatchEvent } from '../services/campaignApi' import { useMatchEvents } from '../hooks/useMatchEvents' import { formatMatchLabel } from '../utils/matchCountdown' import { getCafeUuid } from '../utils/cafe' +import Icon from '../components/Icon' const ENTRY_FEE_STEP = 10000 const DESCRIPTION_MAX_LENGTH = 200 @@ -14,41 +15,6 @@ function toEnglishDigits(value: string) { return value.replace(/[۰-۹]/g, (digit) => String(persianDigits.indexOf(digit))) } -function ChevronIcon({ className = '' }: { className?: string }) { - return ( - - - - ) -} - -function ChevronDownIcon({ className = '' }: { className?: string }) { - return ( - - - - ) -} - -function MinusIcon({ className = '' }: { className?: string }) { - return ( - - - - ) -} - -function PlusIcon({ className = '' }: { className?: string }) { - return ( - - - - ) -} - function MatchSelect({ value, options, @@ -80,7 +46,7 @@ function MatchSelect({ ? 'بازی مورد نظر را انتخاب کنید' : 'بازی فعالی موجود نیست'} - + {open && hasMatches && ( @@ -145,7 +111,7 @@ function Stepper({ disabled={atMin} className={`flex h-6 w-6 items-center justify-center ${atMin ? 'cursor-not-allowed text-[#999999]' : 'text-[#223D5D]'}`} > - + {editable ? ( @@ -170,7 +136,7 @@ function Stepper({ )}

) @@ -227,7 +193,7 @@ export default function AddEvent() {
@@ -255,7 +221,7 @@ export default function AddEvent() { className="pointer-events-auto flex items-center gap-2 rounded-full bg-[#223D5D] px-6 py-3 font-semibold text-white disabled:opacity-50" > {isSubmitting ? 'در حال ارسال...' : 'ادامه'} - {!isSubmitting && } + {!isSubmitting && }
diff --git a/src/pages/AddEventFail.tsx b/src/pages/AddEventFail.tsx index f5a46c2..1875656 100644 --- a/src/pages/AddEventFail.tsx +++ b/src/pages/AddEventFail.tsx @@ -1,5 +1,5 @@ import { useNavigate } from 'react-router-dom' -import FailIcon from '../components/FailIcon' +import Icon from '../components/Icon' export default function AddEventFail() { const navigate = useNavigate() @@ -9,7 +9,7 @@ export default function AddEventFail() {
- +

ثبت ایونت با خطا مواجه شد. لطفا دوباره تلاش کنید.

diff --git a/src/pages/AddEventSuccess.tsx b/src/pages/AddEventSuccess.tsx index 014d3b4..3c15159 100644 --- a/src/pages/AddEventSuccess.tsx +++ b/src/pages/AddEventSuccess.tsx @@ -1,5 +1,5 @@ import { useNavigate } from 'react-router-dom' -import SuccessIcon from '../components/SuccessIcon' +import Icon from '../components/Icon' export default function AddEventSuccess() { const navigate = useNavigate() @@ -9,7 +9,7 @@ export default function AddEventSuccess() {
- +

ایونت با موفقیت ثبت شد!

diff --git a/src/pages/CafeDetail.tsx b/src/pages/CafeDetail.tsx index 457de7f..cd79360 100644 --- a/src/pages/CafeDetail.tsx +++ b/src/pages/CafeDetail.tsx @@ -14,83 +14,9 @@ import { useNow } from "../hooks/useNow"; import { formatMatchLabel } from "../utils/matchCountdown"; import { formatPersianNumber } from "../utils/formatNumber"; import { fromE164 } from "../utils/phone"; -import InfoIcon from "../components/InfoIcon"; +import Icon from "../components/Icon"; import ReservationModal from "../components/ReservationModal"; -function ChevronLeftIcon() { - return ( - - - - ); -} - -function ChevronRightIcon() { - return ( - - - - ); -} - -function PhoneIcon() { - return ( - - - - ); -} - -function MapPinIcon() { - return ( - - - - - ); -} - -function ChevronDownIcon({ className = "" }: { className?: string }) { - return ( - - - - ); -} - function MatchPicker({ value, options, @@ -115,7 +41,7 @@ function MatchPicker({ className="flex items-center gap-1 text-right text-sm text-gray-500" > {selected ? formatMatchLabel(selected.match_event, now) : "بازی را انتخاب کنید"} - + {open && ( @@ -170,7 +96,7 @@ function ImageCarousel({ images }: { images: string[] }) { aria-label="قبلی" className="absolute left-3 top-1/2 flex h-9 w-9 -translate-y-1/2 items-center justify-center rounded-full bg-white shadow" > - +
{Array.from({ length: slideCount }).map((_, i) => ( @@ -261,7 +187,7 @@ export default function CafeDetail() { aria-label="بازگشت" className="absolute left-4 top-4 z-10 flex h-9 w-9 items-center justify-center rounded-full bg-white shadow" > - +
@@ -316,7 +242,7 @@ export default function CafeDetail() {
- +

آدرس

{cafe.address} @@ -342,7 +268,7 @@ export default function CafeDetail() {
- +

توجه داشته باشید که مسئولیت رزرو و حضور به‌موقع در کافه بر عهده کاربر است. diff --git a/src/pages/CafeOwnerPanel.tsx b/src/pages/CafeOwnerPanel.tsx index 48f3927..bf7b68d 100644 --- a/src/pages/CafeOwnerPanel.tsx +++ b/src/pages/CafeOwnerPanel.tsx @@ -1,47 +1,12 @@ import { ReactNode, useEffect, useState } from 'react' import { useNavigate } from 'react-router-dom' -import BookIcon from '../components/BookIcon' +import Icon from '../components/Icon' import { useNow } from '../hooks/useNow' import { CafeSchedule, fetchCafeSchedules } from '../services/campaignApi' import { getCafeUuid } from '../utils/cafe' import { formatPersianNumber } from '../utils/formatNumber' import { formatMatchLabel } from '../utils/matchCountdown' -function ChevronIcon({ className = 'h-5 w-5 text-gray-400' }: { className?: string }) { - return ( - - - - ) -} - -function ProfileCircleIcon() { - return ( - - - - - - ) -} - -function CalendarIcon() { - return ( - - - - - - - - - - - - - ) -} - function PanelRow({ icon, label, onClick }: { icon: ReactNode; label: string; onClick?: () => void }) { return (

- + ) } @@ -76,11 +41,11 @@ export default function CafeOwnerPanel() {
} + icon={} label="اطلاعات کافه" onClick={() => navigate('/owner/register', { state: { step: 2 } })} /> - } label="افزودن رویداد" onClick={() => navigate('/owner/events/new')} /> + } label="افزودن رویداد" onClick={() => navigate('/owner/events/new')} />

رویدادهای فعال شما

@@ -108,7 +73,7 @@ export default function CafeOwnerPanel() {
} + icon={} label="مشاهده رزروها" onClick={() => navigate(`/owner/events/${schedule.uuid}/reservations`, { diff --git a/src/pages/CafeOwnerRegister.tsx b/src/pages/CafeOwnerRegister.tsx index f5bc445..0193dcf 100644 --- a/src/pages/CafeOwnerRegister.tsx +++ b/src/pages/CafeOwnerRegister.tsx @@ -3,7 +3,7 @@ import { useLocation, useNavigate } from "react-router-dom"; import { MapContainer, TileLayer, Marker, ZoomControl, useMapEvents } from "react-leaflet"; import "leaflet/dist/leaflet.css"; import "../utils/leafletIcons"; -import InfoIcon from "../components/InfoIcon"; +import Icon from "../components/Icon"; import { CafeFeature, City, @@ -21,52 +21,6 @@ import { fromE164, toE164 } from "../utils/phone"; // Tehran — default pin until the owner picks their cafe's actual location const DEFAULT_CAFE_LOCATION = { lat: 35.6892, lng: 51.389 }; -function ArrowIcon({ className = "" }: { className?: string }) { - return ( - - - - ); -} - -function ChevronDownIcon({ className = "" }: { className?: string }) { - return ( - - - - ); -} - -function CheckIcon() { - return ( - - - - ); -} - function StepCircle({ active, children, @@ -161,7 +115,10 @@ function CitySelectField({ {selectedCity ? selectedCity.city_name : "شهر کافه را انتخاب کنید"} - + {open && ( @@ -289,7 +246,7 @@ function FeatureChip({ checked ? "bg-[#223D5D]" : "bg-transparent" }`} > - {checked && } + {checked && } {label} @@ -424,7 +381,7 @@ export default function CafeOwnerRegister() { {step === 1 ? ( <>
- +

لطفاً یک{" "} نماینده{" "} @@ -528,7 +485,7 @@ export default function CafeOwnerRegister() { className="pointer-events-auto flex items-center gap-2 rounded-full bg-[#223D5D] px-6 py-3 font-semibold text-white disabled:opacity-50" > {isSubmitting ? "در حال ارسال..." : isEditMode ? "ذخیره تغییرات" : "ادامه"} - {!isSubmitting && !isEditMode && } + {!isSubmitting && !isEditMode && }

diff --git a/src/pages/CafeRegisterFail.tsx b/src/pages/CafeRegisterFail.tsx index 57b8914..50e7909 100644 --- a/src/pages/CafeRegisterFail.tsx +++ b/src/pages/CafeRegisterFail.tsx @@ -1,5 +1,5 @@ import { useNavigate } from 'react-router-dom' -import FailIcon from '../components/FailIcon' +import Icon from '../components/Icon' export default function CafeRegisterFail() { const navigate = useNavigate() @@ -9,7 +9,7 @@ export default function CafeRegisterFail() {
- +

ثبت کافه با خطا مواجه شد. لطفا دوباره تلاش کنید.

diff --git a/src/pages/CafeRegisterSuccess.tsx b/src/pages/CafeRegisterSuccess.tsx index fb5846c..7ae3955 100644 --- a/src/pages/CafeRegisterSuccess.tsx +++ b/src/pages/CafeRegisterSuccess.tsx @@ -1,5 +1,5 @@ import { useNavigate } from 'react-router-dom' -import SuccessIcon from '../components/SuccessIcon' +import Icon from '../components/Icon' export default function CafeRegisterSuccess() { const navigate = useNavigate() @@ -9,7 +9,7 @@ export default function CafeRegisterSuccess() {
- +

کافه شما با موفقیت ثبت شد!

با اضافه کردن ایونت بازی‌ها، کافه شما در کمپین نمایش داده می‌شود و کاربران می‌توانند برای تماشای هر مسابقه در کافه شما، میز رزرو کنند. diff --git a/src/pages/EventReservations.tsx b/src/pages/EventReservations.tsx index 75390cb..709e9ca 100644 --- a/src/pages/EventReservations.tsx +++ b/src/pages/EventReservations.tsx @@ -1,59 +1,11 @@ import { useEffect, useMemo, useState } from "react"; import { useLocation, useNavigate, useParams } from "react-router-dom"; +import Icon from "../components/Icon"; import { CafeEvent, mockCafeEvents } from "../data/mockCafeEvents"; import { EventReservation, fetchEventReservations } from "../services/campaignApi"; import { getCafeUuid } from "../utils/cafe"; import { formatPersianNumber } from "../utils/formatNumber"; -function ChevronIcon({ className = "" }: { className?: string }) { - return ( - - - - ); -} - -function SearchIcon({ className = "" }: { className?: string }) { - return ( - - - - - ); -} - -function CheckIcon() { - return ( - - - - ); -} - function toEnglishDigits(value: string) { const persianDigits = "۰۱۲۳۴۵۶۷۸۹"; return value.replace(/[۰-۹]/g, (digit) => @@ -120,7 +72,7 @@ function ReservationCard({ onClick={onToggle} className={`flex h-6 w-6 shrink-0 items-center justify-center rounded-md border-2 border-[#223D5D] ${selected ? "bg-[#223D5D]" : "bg-transparent"}`} > - {selected && } + {selected && }

); @@ -179,7 +131,7 @@ export default function EventReservations() { onClick={() => navigate(-1)} className="mt-4 flex w-full items-center justify-start gap-2 px-4" > - + رزروهای {event?.matchLabel ?? ""} @@ -193,7 +145,10 @@ export default function EventReservations() { placeholder="جستجو" className="w-full rounded-2xl shadow-[0px_4px_12px_0px_#0000001F] bg-white py-3 pr-6 pl-4 text-right placeholder:font-light placeholder:text-[#999999]" /> - +
diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 2d0e360..c2c61be 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -1,6 +1,6 @@ import { useEffect, useState } from 'react' import { useNavigate } from 'react-router-dom' -import BookIcon from '../components/BookIcon' +import Icon from '../components/Icon' import { Cafe, City, fetchCafes, fetchCities, MatchEvent } from '../services/campaignApi' import { useMatchEvents } from '../hooks/useMatchEvents' import { formatMatchLabel } from '../utils/matchCountdown' @@ -10,28 +10,6 @@ function findTehran(cities: City[]): City | undefined { return cities.find((c) => c.city_name === 'تهران' || c.city_name.toLowerCase() === 'tehran') } -function ChevronIcon({ className = '' }: { className?: string }) { - return ( - - - - ) -} - -function ChevronDownIcon({ className = '' }: { className?: string }) { - return ( - - - - ) -} - // MapIcon/ListIcon — only used by the disabled نقشه/لیست toggle above, commented out together with it // function MapIcon() { // return ( @@ -93,7 +71,10 @@ function CitySelectorRow({ className="w-full flex items-center justify-between gap-2 rounded-xl border border-gray-200 bg-white px-4 py-3" > {selectedCity?.city_name ?? ''} - + {open && ( @@ -149,7 +130,10 @@ function MatchSelectorRow({ {selectedMatch ? formatMatchLabel(selectedMatch, now) : !hasMatches ? 'بازی فعالی موجود نیست' : ''} - + {open && hasMatches && ( @@ -186,10 +170,10 @@ function MyReservationsRow() { className="flex w-full items-center justify-between px-4 py-6" >
- + رزروهای شما
- + ) } diff --git a/src/pages/Login.tsx b/src/pages/Login.tsx index 499092c..7542bab 100644 --- a/src/pages/Login.tsx +++ b/src/pages/Login.tsx @@ -1,26 +1,11 @@ import { useEffect, useState } from "react"; import { useLocation, useNavigate } from "react-router-dom"; +import Icon from "../components/Icon"; import { requestOtp } from "../services/authApi"; import { toPersianDigits } from "../utils/formatNumber"; import { toE164 } from "../utils/phone"; import { setUserRole } from "../utils/role"; -function CheckIcon() { - return ( - - - - ); -} - export default function Login() { const navigate = useNavigate(); const location = useLocation(); @@ -98,7 +83,7 @@ export default function Login() { acceptedTerms ? "bg-[#223D5D]" : "bg-transparent" }`} > - {acceptedTerms && } + {acceptedTerms && }

diff --git a/src/pages/LoginOtp.tsx b/src/pages/LoginOtp.tsx index ef70874..8943688 100644 --- a/src/pages/LoginOtp.tsx +++ b/src/pages/LoginOtp.tsx @@ -1,5 +1,6 @@ import { useEffect, useRef, useState } from 'react' import { useLocation, useNavigate } from 'react-router-dom' +import Icon from '../components/Icon' import { requestOtp, verifyOtp } from '../services/authApi' import { fetchMyCafes } from '../services/campaignApi' import { storeAuthTokens } from '../utils/authToken' @@ -17,19 +18,6 @@ interface OTPCredential extends Credential { code: string } -function EditIcon({ className = '' }: { className?: string }) { - return ( - - - - - - ) -} - function formatCountdown(seconds: number) { const mm = String(Math.floor(seconds / 60)).padStart(2, '0') const ss = String(seconds % 60).padStart(2, '0') @@ -195,7 +183,7 @@ export default function LoginOtp() {

diff --git a/src/pages/MyReservations.tsx b/src/pages/MyReservations.tsx index 643b2e0..3e14bd7 100644 --- a/src/pages/MyReservations.tsx +++ b/src/pages/MyReservations.tsx @@ -1,8 +1,7 @@ import { useEffect, useState } from 'react' import { createPortal } from 'react-dom' import { useNavigate } from 'react-router-dom' -import InfoIcon from '../components/InfoIcon' -import SuccessIcon from '../components/SuccessIcon' +import Icon from '../components/Icon' import { fetchMyReservationByUuid, fetchMyReservations, MyReservation } from '../services/campaignApi' import { formatPersianNumber } from '../utils/formatNumber' @@ -16,14 +15,6 @@ function formatReservationDateTime(iso: string): string { }).format(new Date(iso)) } -function ChevronIcon({ className = '' }: { className?: string }) { - return ( - - - - ) -} - function MyReservationCard({ reservation, onClick }: { reservation: MyReservation; onClick: () => void }) { return ( diff --git a/src/pages/PaymentSuccess.tsx b/src/pages/PaymentSuccess.tsx index 001ffb5..20e7467 100644 --- a/src/pages/PaymentSuccess.tsx +++ b/src/pages/PaymentSuccess.tsx @@ -1,8 +1,7 @@ import { useEffect, useState } from 'react' import { useNavigate, useSearchParams } from 'react-router-dom' -import InfoIcon from '../components/InfoIcon' +import Icon from '../components/Icon' import PaymentFailModal from '../components/PaymentFailModal' -import SuccessIcon from '../components/SuccessIcon' import { createReservation, ReservationResult } from '../services/campaignApi' import { formatPersianNumber } from '../utils/formatNumber' import { fromE164 } from '../utils/phone' @@ -48,7 +47,7 @@ export default function PaymentSuccess() { {!isFail && (
- + {reservation ? ( <> @@ -61,7 +60,7 @@ export default function PaymentSuccess() {
- +

کد رزرو را برای ورود نزد خود نگه دارید. اطلاعات رزرو و کد ورود تا دقایقی دیگر برای شما پیامک می‌شود.

diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts new file mode 100644 index 0000000..b1f45c7 --- /dev/null +++ b/src/vite-env.d.ts @@ -0,0 +1,2 @@ +/// +/// diff --git a/vite.config.js b/vite.config.js index c4069b7..1ea70d6 100644 --- a/vite.config.js +++ b/vite.config.js @@ -1,8 +1,11 @@ import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import tailwindcss from '@tailwindcss/vite' +import svgr from 'vite-plugin-svgr' // https://vite.dev/config/ export default defineConfig({ - plugins: [react(), tailwindcss()], + // svgr only transforms imports ending in `?react` (e.g. `import Foo from './foo.svg?react'`), + // so plain `?url`/default `.svg` imports elsewhere (favicons, , etc.) are untouched + plugins: [react(), tailwindcss(), svgr()], })