[dyad] wrote 5 file(s), added jspdf package(s)
This commit is contained in:
parent
883981a4be
commit
afe1727b77
5 changed files with 466 additions and 112 deletions
|
|
@ -48,6 +48,7 @@
|
|||
"embla-carousel-react": "^8.3.0",
|
||||
"html5-qrcode": "^2.3.8",
|
||||
"input-otp": "^1.2.4",
|
||||
"jspdf": "^3.0.2",
|
||||
"lucide-react": "^0.462.0",
|
||||
"next-themes": "^0.3.0",
|
||||
"pocketbase": "^0.26.2",
|
||||
|
|
|
|||
170
pnpm-lock.yaml
170
pnpm-lock.yaml
|
|
@ -119,6 +119,9 @@ importers:
|
|||
input-otp:
|
||||
specifier: ^1.2.4
|
||||
version: 1.4.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
jspdf:
|
||||
specifier: ^3.0.2
|
||||
version: 3.0.2
|
||||
lucide-react:
|
||||
specifier: ^0.462.0
|
||||
version: 0.462.0(react@18.3.1)
|
||||
|
|
@ -1374,9 +1377,15 @@ packages:
|
|||
'@types/node@22.14.1':
|
||||
resolution: {integrity: sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==}
|
||||
|
||||
'@types/pako@2.0.4':
|
||||
resolution: {integrity: sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw==}
|
||||
|
||||
'@types/prop-types@15.7.14':
|
||||
resolution: {integrity: sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==}
|
||||
|
||||
'@types/raf@3.4.3':
|
||||
resolution: {integrity: sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==}
|
||||
|
||||
'@types/react-dom@18.3.6':
|
||||
resolution: {integrity: sha512-nf22//wEbKXusP6E9pfOCDwFdHAX4u172eaJI4YkDRQEZiorm6KfYnSC2SWLDMVWUOWPERmJnN0ujeAfTBLvrw==}
|
||||
peerDependencies:
|
||||
|
|
@ -1385,6 +1394,9 @@ packages:
|
|||
'@types/react@18.3.20':
|
||||
resolution: {integrity: sha512-IPaCZN7PShZK/3t6Q87pfTkRm6oLTd4vztyoj+cbHUF1g3FfVb2tFIL79uCRKEfv16AhqDMBywP2VW3KIZUvcg==}
|
||||
|
||||
'@types/trusted-types@2.0.7':
|
||||
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
|
||||
|
||||
'@typescript-eslint/eslint-plugin@8.30.1':
|
||||
resolution: {integrity: sha512-v+VWphxMjn+1t48/jO4t950D6KR8JaJuNXzi33Ve6P8sEmPr5k6CEXjdGwT6+LodVnEa91EQCtwjWNUCPweo+Q==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
|
@ -1493,6 +1505,10 @@ packages:
|
|||
balanced-match@1.0.2:
|
||||
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
||||
|
||||
base64-arraybuffer@1.0.2:
|
||||
resolution: {integrity: sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==}
|
||||
engines: {node: '>= 0.6.0'}
|
||||
|
||||
binary-extensions@2.3.0:
|
||||
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
|
||||
engines: {node: '>=8'}
|
||||
|
|
@ -1523,6 +1539,10 @@ packages:
|
|||
caniuse-lite@1.0.30001714:
|
||||
resolution: {integrity: sha512-mtgapdwDLSSBnCI3JokHM7oEQBLxiJKVRtg10AxM1AyeiKcM96f0Mkbqeq+1AbiCtvMcHRulAAEMu693JrSWqg==}
|
||||
|
||||
canvg@3.0.11:
|
||||
resolution: {integrity: sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
|
||||
chalk@4.1.2:
|
||||
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
|
||||
engines: {node: '>=10'}
|
||||
|
|
@ -1558,10 +1578,16 @@ packages:
|
|||
concat-map@0.0.1:
|
||||
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
|
||||
|
||||
core-js@3.45.1:
|
||||
resolution: {integrity: sha512-L4NPsJlCfZsPeXukyzHFlg/i7IIVwHSItR0wg0FLNqYClJ4MQYTYLbC7EkjKYRLZF2iof2MUgN0EGy7MdQFChg==}
|
||||
|
||||
cross-spawn@7.0.6:
|
||||
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
css-line-break@2.1.0:
|
||||
resolution: {integrity: sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==}
|
||||
|
||||
cssesc@3.0.0:
|
||||
resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
|
||||
engines: {node: '>=4'}
|
||||
|
|
@ -1644,6 +1670,9 @@ packages:
|
|||
dom-helpers@5.2.1:
|
||||
resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==}
|
||||
|
||||
dompurify@3.2.6:
|
||||
resolution: {integrity: sha512-/2GogDQlohXPZe6D6NOgQvXLPSYBqIWMnZ8zzOhn09REE4eyAzb+Hed3jhoM9OkuaJ8P6ZGTTVWQKAi8ieIzfQ==}
|
||||
|
||||
eastasianwidth@0.2.0:
|
||||
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
|
||||
|
||||
|
|
@ -1758,6 +1787,9 @@ packages:
|
|||
fast-levenshtein@2.0.6:
|
||||
resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
|
||||
|
||||
fast-png@6.4.0:
|
||||
resolution: {integrity: sha512-kAqZq1TlgBjZcLr5mcN6NP5Rv4V2f22z00c3g8vRrwkcqjerx7BEhPbOnWCPqaHUl2XWQBJQvOT/FQhdMT7X/Q==}
|
||||
|
||||
fastq@1.19.1:
|
||||
resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==}
|
||||
|
||||
|
|
@ -1769,6 +1801,9 @@ packages:
|
|||
picomatch:
|
||||
optional: true
|
||||
|
||||
fflate@0.8.2:
|
||||
resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==}
|
||||
|
||||
file-entry-cache@8.0.0:
|
||||
resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
|
||||
engines: {node: '>=16.0.0'}
|
||||
|
|
@ -1843,6 +1878,10 @@ packages:
|
|||
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
html2canvas@1.4.1:
|
||||
resolution: {integrity: sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==}
|
||||
engines: {node: '>=8.0.0'}
|
||||
|
||||
html5-qrcode@2.3.8:
|
||||
resolution: {integrity: sha512-jsr4vafJhwoLVEDW3n1KvPnCCXWaQfRng0/EEYk1vNcQGcG/htAdhJX0be8YyqMoSz7+hZvOZSTAepsabiuhiQ==}
|
||||
|
||||
|
|
@ -1868,6 +1907,9 @@ packages:
|
|||
resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
iobuffer@5.4.0:
|
||||
resolution: {integrity: sha512-DRebOWuqDvxunfkNJAlc3IzWIPD5xVxwUNbHr7xKB8E6aLJxIPfNX3CoMJghcFjpv6RWQsrcJbghtEwSPoJqMA==}
|
||||
|
||||
is-binary-path@2.1.0:
|
||||
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
|
||||
engines: {node: '>=8'}
|
||||
|
|
@ -1921,6 +1963,9 @@ packages:
|
|||
json-stable-stringify-without-jsonify@1.0.1:
|
||||
resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
|
||||
|
||||
jspdf@3.0.2:
|
||||
resolution: {integrity: sha512-G0fQDJ5fAm6UW78HG6lNXyq09l0PrA1rpNY5i+ly17Zb1fMMFSmS+3lw4cnrAPGyouv2Y0ylujbY2Ieq3DSlKA==}
|
||||
|
||||
keyv@4.5.4:
|
||||
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
|
||||
|
||||
|
|
@ -2039,6 +2084,9 @@ packages:
|
|||
package-json-from-dist@1.0.1:
|
||||
resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
|
||||
|
||||
pako@2.1.0:
|
||||
resolution: {integrity: sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==}
|
||||
|
||||
parent-module@1.0.1:
|
||||
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
|
||||
engines: {node: '>=6'}
|
||||
|
|
@ -2058,6 +2106,9 @@ packages:
|
|||
resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
|
||||
engines: {node: '>=16 || 14 >=14.18'}
|
||||
|
||||
performance-now@2.1.0:
|
||||
resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==}
|
||||
|
||||
picocolors@1.1.1:
|
||||
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
||||
|
||||
|
|
@ -2144,6 +2195,9 @@ packages:
|
|||
queue-microtask@1.2.3:
|
||||
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
||||
|
||||
raf@3.4.1:
|
||||
resolution: {integrity: sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==}
|
||||
|
||||
react-barcode@1.6.1:
|
||||
resolution: {integrity: sha512-pc4ftnO5syHa/UjCruEeRsomlhoxKSugIgTA8T4dH0fvc89UMHL+/1Sp25IAphqG44pJkE5hMXhv89iS09jQyw==}
|
||||
peerDependencies:
|
||||
|
|
@ -2261,6 +2315,9 @@ packages:
|
|||
react: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
|
||||
regenerator-runtime@0.13.11:
|
||||
resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==}
|
||||
|
||||
regenerator-runtime@0.14.1:
|
||||
resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
|
||||
|
||||
|
|
@ -2277,6 +2334,10 @@ packages:
|
|||
resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
|
||||
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
|
||||
|
||||
rgbcolor@1.0.1:
|
||||
resolution: {integrity: sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==}
|
||||
engines: {node: '>= 0.8.15'}
|
||||
|
||||
rollup@4.40.0:
|
||||
resolution: {integrity: sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w==}
|
||||
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
||||
|
|
@ -2315,6 +2376,10 @@ packages:
|
|||
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
stackblur-canvas@2.7.0:
|
||||
resolution: {integrity: sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==}
|
||||
engines: {node: '>=0.1.14'}
|
||||
|
||||
string-width@4.2.3:
|
||||
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
|
||||
engines: {node: '>=8'}
|
||||
|
|
@ -2348,6 +2413,10 @@ packages:
|
|||
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
svg-pathdata@6.0.3:
|
||||
resolution: {integrity: sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
||||
tailwind-merge@2.6.0:
|
||||
resolution: {integrity: sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==}
|
||||
|
||||
|
|
@ -2361,6 +2430,9 @@ packages:
|
|||
engines: {node: '>=14.0.0'}
|
||||
hasBin: true
|
||||
|
||||
text-segmentation@1.0.3:
|
||||
resolution: {integrity: sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==}
|
||||
|
||||
thenify-all@1.6.0:
|
||||
resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==}
|
||||
engines: {node: '>=0.8'}
|
||||
|
|
@ -2442,6 +2514,9 @@ packages:
|
|||
util-deprecate@1.0.2:
|
||||
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
||||
|
||||
utrie@1.0.2:
|
||||
resolution: {integrity: sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==}
|
||||
|
||||
vaul@0.9.9:
|
||||
resolution: {integrity: sha512-7afKg48srluhZwIkaU+lgGtFCUsYBSGOl8vcc8N/M3YQlZFlynHD15AE+pwrYdc826o7nrIND4lL9Y6b9WWZZQ==}
|
||||
peerDependencies:
|
||||
|
|
@ -3572,8 +3647,13 @@ snapshots:
|
|||
dependencies:
|
||||
undici-types: 6.21.0
|
||||
|
||||
'@types/pako@2.0.4': {}
|
||||
|
||||
'@types/prop-types@15.7.14': {}
|
||||
|
||||
'@types/raf@3.4.3':
|
||||
optional: true
|
||||
|
||||
'@types/react-dom@18.3.6(@types/react@18.3.20)':
|
||||
dependencies:
|
||||
'@types/react': 18.3.20
|
||||
|
|
@ -3583,6 +3663,9 @@ snapshots:
|
|||
'@types/prop-types': 15.7.14
|
||||
csstype: 3.1.3
|
||||
|
||||
'@types/trusted-types@2.0.7':
|
||||
optional: true
|
||||
|
||||
'@typescript-eslint/eslint-plugin@8.30.1(@typescript-eslint/parser@8.30.1(eslint@9.24.0(jiti@1.21.7))(typescript@5.8.3))(eslint@9.24.0(jiti@1.21.7))(typescript@5.8.3)':
|
||||
dependencies:
|
||||
'@eslint-community/regexpp': 4.12.1
|
||||
|
|
@ -3717,6 +3800,9 @@ snapshots:
|
|||
|
||||
balanced-match@1.0.2: {}
|
||||
|
||||
base64-arraybuffer@1.0.2:
|
||||
optional: true
|
||||
|
||||
binary-extensions@2.3.0: {}
|
||||
|
||||
brace-expansion@1.1.11:
|
||||
|
|
@ -3745,6 +3831,18 @@ snapshots:
|
|||
|
||||
caniuse-lite@1.0.30001714: {}
|
||||
|
||||
canvg@3.0.11:
|
||||
dependencies:
|
||||
'@babel/runtime': 7.27.0
|
||||
'@types/raf': 3.4.3
|
||||
core-js: 3.45.1
|
||||
raf: 3.4.1
|
||||
regenerator-runtime: 0.13.11
|
||||
rgbcolor: 1.0.1
|
||||
stackblur-canvas: 2.7.0
|
||||
svg-pathdata: 6.0.3
|
||||
optional: true
|
||||
|
||||
chalk@4.1.2:
|
||||
dependencies:
|
||||
ansi-styles: 4.3.0
|
||||
|
|
@ -3790,12 +3888,20 @@ snapshots:
|
|||
|
||||
concat-map@0.0.1: {}
|
||||
|
||||
core-js@3.45.1:
|
||||
optional: true
|
||||
|
||||
cross-spawn@7.0.6:
|
||||
dependencies:
|
||||
path-key: 3.1.1
|
||||
shebang-command: 2.0.0
|
||||
which: 2.0.2
|
||||
|
||||
css-line-break@2.1.0:
|
||||
dependencies:
|
||||
utrie: 1.0.2
|
||||
optional: true
|
||||
|
||||
cssesc@3.0.0: {}
|
||||
|
||||
csstype@3.1.3: {}
|
||||
|
|
@ -3859,6 +3965,11 @@ snapshots:
|
|||
'@babel/runtime': 7.27.0
|
||||
csstype: 3.1.3
|
||||
|
||||
dompurify@3.2.6:
|
||||
optionalDependencies:
|
||||
'@types/trusted-types': 2.0.7
|
||||
optional: true
|
||||
|
||||
eastasianwidth@0.2.0: {}
|
||||
|
||||
electron-to-chromium@1.5.138: {}
|
||||
|
|
@ -4008,6 +4119,12 @@ snapshots:
|
|||
|
||||
fast-levenshtein@2.0.6: {}
|
||||
|
||||
fast-png@6.4.0:
|
||||
dependencies:
|
||||
'@types/pako': 2.0.4
|
||||
iobuffer: 5.4.0
|
||||
pako: 2.1.0
|
||||
|
||||
fastq@1.19.1:
|
||||
dependencies:
|
||||
reusify: 1.1.0
|
||||
|
|
@ -4016,6 +4133,8 @@ snapshots:
|
|||
optionalDependencies:
|
||||
picomatch: 4.0.2
|
||||
|
||||
fflate@0.8.2: {}
|
||||
|
||||
file-entry-cache@8.0.0:
|
||||
dependencies:
|
||||
flat-cache: 4.0.1
|
||||
|
|
@ -4083,6 +4202,12 @@ snapshots:
|
|||
dependencies:
|
||||
function-bind: 1.1.2
|
||||
|
||||
html2canvas@1.4.1:
|
||||
dependencies:
|
||||
css-line-break: 2.1.0
|
||||
text-segmentation: 1.0.3
|
||||
optional: true
|
||||
|
||||
html5-qrcode@2.3.8: {}
|
||||
|
||||
ignore@5.3.2: {}
|
||||
|
|
@ -4101,6 +4226,8 @@ snapshots:
|
|||
|
||||
internmap@2.0.3: {}
|
||||
|
||||
iobuffer@5.4.0: {}
|
||||
|
||||
is-binary-path@2.1.0:
|
||||
dependencies:
|
||||
binary-extensions: 2.3.0
|
||||
|
|
@ -4143,6 +4270,17 @@ snapshots:
|
|||
|
||||
json-stable-stringify-without-jsonify@1.0.1: {}
|
||||
|
||||
jspdf@3.0.2:
|
||||
dependencies:
|
||||
'@babel/runtime': 7.27.0
|
||||
fast-png: 6.4.0
|
||||
fflate: 0.8.2
|
||||
optionalDependencies:
|
||||
canvg: 3.0.11
|
||||
core-js: 3.45.1
|
||||
dompurify: 3.2.6
|
||||
html2canvas: 1.4.1
|
||||
|
||||
keyv@4.5.4:
|
||||
dependencies:
|
||||
json-buffer: 3.0.1
|
||||
|
|
@ -4245,6 +4383,8 @@ snapshots:
|
|||
|
||||
package-json-from-dist@1.0.1: {}
|
||||
|
||||
pako@2.1.0: {}
|
||||
|
||||
parent-module@1.0.1:
|
||||
dependencies:
|
||||
callsites: 3.1.0
|
||||
|
|
@ -4260,6 +4400,9 @@ snapshots:
|
|||
lru-cache: 10.4.3
|
||||
minipass: 7.1.2
|
||||
|
||||
performance-now@2.1.0:
|
||||
optional: true
|
||||
|
||||
picocolors@1.1.1: {}
|
||||
|
||||
picomatch@2.3.1: {}
|
||||
|
|
@ -4330,6 +4473,11 @@ snapshots:
|
|||
|
||||
queue-microtask@1.2.3: {}
|
||||
|
||||
raf@3.4.1:
|
||||
dependencies:
|
||||
performance-now: 2.1.0
|
||||
optional: true
|
||||
|
||||
react-barcode@1.6.1(react@18.3.1):
|
||||
dependencies:
|
||||
jsbarcode: 3.12.1
|
||||
|
|
@ -4452,6 +4600,9 @@ snapshots:
|
|||
tiny-invariant: 1.3.3
|
||||
victory-vendor: 36.9.2
|
||||
|
||||
regenerator-runtime@0.13.11:
|
||||
optional: true
|
||||
|
||||
regenerator-runtime@0.14.1: {}
|
||||
|
||||
resolve-from@4.0.0: {}
|
||||
|
|
@ -4464,6 +4615,9 @@ snapshots:
|
|||
|
||||
reusify@1.1.0: {}
|
||||
|
||||
rgbcolor@1.0.1:
|
||||
optional: true
|
||||
|
||||
rollup@4.40.0:
|
||||
dependencies:
|
||||
'@types/estree': 1.0.7
|
||||
|
|
@ -4515,6 +4669,9 @@ snapshots:
|
|||
|
||||
source-map-js@1.2.1: {}
|
||||
|
||||
stackblur-canvas@2.7.0:
|
||||
optional: true
|
||||
|
||||
string-width@4.2.3:
|
||||
dependencies:
|
||||
emoji-regex: 8.0.0
|
||||
|
|
@ -4553,6 +4710,9 @@ snapshots:
|
|||
|
||||
supports-preserve-symlinks-flag@1.0.0: {}
|
||||
|
||||
svg-pathdata@6.0.3:
|
||||
optional: true
|
||||
|
||||
tailwind-merge@2.6.0: {}
|
||||
|
||||
tailwindcss-animate@1.0.7(tailwindcss@3.4.17):
|
||||
|
|
@ -4586,6 +4746,11 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- ts-node
|
||||
|
||||
text-segmentation@1.0.3:
|
||||
dependencies:
|
||||
utrie: 1.0.2
|
||||
optional: true
|
||||
|
||||
thenify-all@1.6.0:
|
||||
dependencies:
|
||||
thenify: 3.3.1
|
||||
|
|
@ -4658,6 +4823,11 @@ snapshots:
|
|||
|
||||
util-deprecate@1.0.2: {}
|
||||
|
||||
utrie@1.0.2:
|
||||
dependencies:
|
||||
base64-arraybuffer: 1.0.2
|
||||
optional: true
|
||||
|
||||
vaul@0.9.9(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
||||
dependencies:
|
||||
'@radix-ui/react-dialog': 1.1.7(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
|
|
|
|||
|
|
@ -14,23 +14,15 @@ import {
|
|||
useReactTable,
|
||||
} from "@tanstack/react-table"
|
||||
import { Columns } from "lucide-react"
|
||||
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu"
|
||||
import { jsPDF } from "jspdf"
|
||||
import { ReceiveStockDialog } from "./ReceiveStockDialog"
|
||||
|
||||
export type PurchaseOrder = {
|
||||
id: string
|
||||
|
|
@ -38,6 +30,8 @@ export type PurchaseOrder = {
|
|||
orderDate: string
|
||||
status: string
|
||||
itemCount: number
|
||||
items: Array<{ stockItemId: string; itemName: string; quantity: number }>
|
||||
quantities: number[]
|
||||
}
|
||||
|
||||
interface PurchaseOrderDataTableProps<TData, TValue> {
|
||||
|
|
@ -45,6 +39,7 @@ interface PurchaseOrderDataTableProps<TData, TValue> {
|
|||
data: TData[]
|
||||
filterValue: string
|
||||
onFilterChange: (value: string) => void
|
||||
refreshData: () => void
|
||||
}
|
||||
|
||||
export function PurchaseOrderDataTable<TData, TValue>({
|
||||
|
|
@ -52,6 +47,7 @@ export function PurchaseOrderDataTable<TData, TValue>({
|
|||
data,
|
||||
filterValue,
|
||||
onFilterChange,
|
||||
refreshData,
|
||||
}: PurchaseOrderDataTableProps<TData, TValue>) {
|
||||
const [sorting, setSorting] = React.useState<SortingState>([])
|
||||
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>([])
|
||||
|
|
@ -66,6 +62,55 @@ export function PurchaseOrderDataTable<TData, TValue>({
|
|||
return {};
|
||||
});
|
||||
const [rowSelection, setRowSelection] = React.useState({})
|
||||
const [receiveDialogOpen, setReceiveDialogOpen] = React.useState(false)
|
||||
const [selectedOrder, setSelectedOrder] = React.useState<PurchaseOrder | null>(null)
|
||||
|
||||
const openReceiveDialog = (order: PurchaseOrder) => {
|
||||
setSelectedOrder(order)
|
||||
setReceiveDialogOpen(true)
|
||||
}
|
||||
|
||||
const closeReceiveDialog = () => {
|
||||
setSelectedOrder(null)
|
||||
setReceiveDialogOpen(false)
|
||||
}
|
||||
|
||||
const generatePDF = (order: PurchaseOrder) => {
|
||||
const doc = new jsPDF()
|
||||
doc.setFontSize(16)
|
||||
doc.text("Purchase Order", 14, 20)
|
||||
doc.setFontSize(12)
|
||||
doc.text(`Supplier: ${order.supplierName}`, 14, 30)
|
||||
doc.text(`Order Date: ${new Date(order.orderDate).toLocaleDateString()}`, 14, 40)
|
||||
doc.text(`Status: ${order.status}`, 14, 50)
|
||||
doc.text("Items:", 14, 60)
|
||||
|
||||
let y = 70
|
||||
order.items.forEach((item, index) => {
|
||||
doc.text(`${index + 1}. ${item.itemName} - Qty: ${item.quantity}`, 14, y)
|
||||
y += 10
|
||||
})
|
||||
|
||||
doc.save(`purchase_order_${order.id}.pdf`)
|
||||
}
|
||||
|
||||
const approveOrder = async (order: PurchaseOrder) => {
|
||||
try {
|
||||
await pb.collection('purchase_orders').update(order.id, { status: "Approved" })
|
||||
refreshData()
|
||||
} catch (error) {
|
||||
console.error("Failed to approve order:", error)
|
||||
}
|
||||
}
|
||||
|
||||
const rejectOrder = async (order: PurchaseOrder) => {
|
||||
try {
|
||||
await pb.collection('purchase_orders').update(order.id, { status: "Rejected" })
|
||||
refreshData()
|
||||
} catch (error) {
|
||||
console.error("Failed to reject order:", error)
|
||||
}
|
||||
}
|
||||
|
||||
const table = useReactTable({
|
||||
data,
|
||||
|
|
@ -92,110 +137,116 @@ export function PurchaseOrderDataTable<TData, TValue>({
|
|||
}, [filterValue, table])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="flex items-center py-4">
|
||||
<Input
|
||||
placeholder="Filter by supplier name..."
|
||||
value={filterValue}
|
||||
onChange={(event) => onFilterChange(event.target.value)}
|
||||
className="max-w-sm"
|
||||
/>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="outline" size="icon" className="ml-auto">
|
||||
<Columns className="h-4 w-4" />
|
||||
<span className="sr-only">Toggle columns</span>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
{table
|
||||
.getAllColumns()
|
||||
.filter(
|
||||
(column) => column.getCanHide()
|
||||
)
|
||||
.map((column) => {
|
||||
return (
|
||||
<DropdownMenuCheckboxItem
|
||||
key={column.id}
|
||||
className="capitalize"
|
||||
checked={column.getIsVisible()}
|
||||
onCheckedChange={(value) =>
|
||||
column.toggleVisibility(!!value)
|
||||
}
|
||||
>
|
||||
{column.id}
|
||||
</DropdownMenuCheckboxItem>
|
||||
<>
|
||||
<div>
|
||||
<div className="flex items-center py-4">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Filter by supplier name..."
|
||||
value={filterValue}
|
||||
onChange={(e) => onFilterChange(e.target.value)}
|
||||
className="max-w-sm border border-gray-300 rounded px-2 py-1"
|
||||
/>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="outline" size="icon" className="ml-auto">
|
||||
<Columns className="h-4 w-4" />
|
||||
<span className="sr-only">Toggle columns</span>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
{table
|
||||
.getAllColumns()
|
||||
.filter(
|
||||
(column) => column.getCanHide()
|
||||
)
|
||||
})}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
<div className="rounded-md border bg-white">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<TableRow key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => {
|
||||
.map((column) => {
|
||||
return (
|
||||
<TableHead key={header.id}>
|
||||
{header.isPlaceholder
|
||||
? null
|
||||
: flexRender(
|
||||
header.column.columnDef.header,
|
||||
header.getContext()
|
||||
)}
|
||||
</TableHead>
|
||||
<DropdownMenuCheckboxItem
|
||||
key={column.id}
|
||||
className="capitalize"
|
||||
checked={column.getIsVisible()}
|
||||
onCheckedChange={(value) =>
|
||||
column.toggleVisibility(!!value)
|
||||
}
|
||||
>
|
||||
{column.id}
|
||||
</DropdownMenuCheckboxItem>
|
||||
)
|
||||
})}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{table.getRowModel().rows?.length ? (
|
||||
table.getRowModel().rows.map((row) => (
|
||||
<TableRow
|
||||
key={row.id}
|
||||
data-state={row.getIsSelected() && "selected"}
|
||||
>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<TableCell key={cell.id}>
|
||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell colSpan={columns.length} className="h-24 text-center">
|
||||
No results.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
<div className="flex items-center justify-end space-x-2 py-4">
|
||||
<div className="flex-1 text-sm text-muted-foreground">
|
||||
{table.getFilteredSelectedRowModel().rows.length} of{" "}
|
||||
{table.getFilteredRowModel().rows.length} row(s) selected.
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
<div className="rounded-md border bg-white">
|
||||
<table className="w-full min-w-full table-auto">
|
||||
<thead>
|
||||
{table.getHeaderGroups().map(headerGroup => (
|
||||
<tr key={headerGroup.id}>
|
||||
{headerGroup.headers.map(header => (
|
||||
<th key={header.id} className="border-b px-4 py-2 text-left font-medium text-gray-900">
|
||||
{header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</thead>
|
||||
<tbody>
|
||||
{table.getRowModel().rows.length ? (
|
||||
table.getRowModel().rows.map(row => {
|
||||
const order = row.original as PurchaseOrder
|
||||
return (
|
||||
<tr key={row.id} className="border-b hover:bg-gray-50">
|
||||
{row.getVisibleCells().map(cell => (
|
||||
<td key={cell.id} className="px-4 py-2 align-top">
|
||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||
</td>
|
||||
))}
|
||||
<td className="px-4 py-2 space-x-2">
|
||||
{order.status === "Pending Approval" && (
|
||||
<>
|
||||
<Button size="sm" variant="outline" onClick={() => approveOrder(order)}>Approve</Button>
|
||||
<Button size="sm" variant="destructive" onClick={() => rejectOrder(order)}>Reject</Button>
|
||||
</>
|
||||
)}
|
||||
{order.status === "Approved" && (
|
||||
<>
|
||||
<Button size="sm" onClick={() => generatePDF(order)}>Download PDF</Button>
|
||||
<Button size="sm" variant="secondary" onClick={() => openReceiveDialog(order)}>Receive Stock</Button>
|
||||
</>
|
||||
)}
|
||||
{order.status === "Received" && (
|
||||
<span className="text-green-600 font-semibold">Received</span>
|
||||
)}
|
||||
{order.status === "Rejected" && (
|
||||
<span className="text-red-600 font-semibold">Rejected</span>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
})
|
||||
) : (
|
||||
<tr>
|
||||
<td colSpan={columns.length + 1} className="h-24 text-center">No results.</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => table.previousPage()}
|
||||
disabled={!table.getCanPreviousPage()}
|
||||
>
|
||||
Previous
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => table.nextPage()}
|
||||
disabled={!table.getCanNextPage()}
|
||||
>
|
||||
Next
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{selectedOrder && (
|
||||
<ReceiveStockDialog
|
||||
open={receiveDialogOpen}
|
||||
onOpenChange={setReceiveDialogOpen}
|
||||
purchaseOrderId={selectedOrder.id}
|
||||
items={selectedOrder.items.map((item, idx) => ({
|
||||
stockItemId: item.stockItemId,
|
||||
orderedQuantity: selectedOrder.quantities[idx] || 0,
|
||||
itemName: item.itemName,
|
||||
}))}
|
||||
refreshData={refreshData}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
124
src/components/purchase-orders/ReceiveStockDialog.tsx
Normal file
124
src/components/purchase-orders/ReceiveStockDialog.tsx
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from "@/components/ui/dialog"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { useForm, useFieldArray } from "react-hook-form"
|
||||
import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import * as z from "zod"
|
||||
import pb from "@/lib/pocketbase"
|
||||
import { showSuccess, showError } from "@/utils/toast"
|
||||
|
||||
const formSchema = z.object({
|
||||
receivedItems: z.array(z.object({
|
||||
stockItemId: z.string(),
|
||||
orderedQuantity: z.number().int().nonnegative(),
|
||||
receivedQuantity: z.number().int().nonnegative(),
|
||||
})).min(1),
|
||||
})
|
||||
|
||||
type FormSchema = z.infer<typeof formSchema>
|
||||
|
||||
interface ReceiveStockDialogProps {
|
||||
open: boolean
|
||||
onOpenChange: (open: boolean) => void
|
||||
purchaseOrderId: string
|
||||
items: Array<{ stockItemId: string; orderedQuantity: number; itemName: string }>
|
||||
refreshData: () => void
|
||||
}
|
||||
|
||||
export function ReceiveStockDialog({ open, onOpenChange, purchaseOrderId, items, refreshData }: ReceiveStockDialogProps) {
|
||||
const form = useForm<FormSchema>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
receivedItems: items.map(i => ({
|
||||
stockItemId: i.stockItemId,
|
||||
orderedQuantity: i.orderedQuantity,
|
||||
receivedQuantity: i.orderedQuantity,
|
||||
})),
|
||||
},
|
||||
})
|
||||
|
||||
const { fields } = useFieldArray({
|
||||
control: form.control,
|
||||
name: "receivedItems",
|
||||
})
|
||||
|
||||
async function onSubmit(values: FormSchema) {
|
||||
try {
|
||||
// Update stock quantities and purchase order status
|
||||
for (const item of values.receivedItems) {
|
||||
if (item.receivedQuantity > 0) {
|
||||
// Fetch current stock item
|
||||
const stockItem = await pb.collection('stock_items').getOne(item.stockItemId)
|
||||
if (stockItem) {
|
||||
// Update quantity by adding receivedQuantity
|
||||
const newQuantity = (stockItem.quantity || 0) + item.receivedQuantity
|
||||
await pb.collection('stock_items').update(item.stockItemId, { quantity: newQuantity })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update purchase order status to 'Received'
|
||||
await pb.collection('purchase_orders').update(purchaseOrderId, { status: "Received" })
|
||||
|
||||
showSuccess("Stock received and purchase order updated successfully!")
|
||||
onOpenChange(false)
|
||||
refreshData()
|
||||
} catch (error) {
|
||||
console.error("Error receiving stock:", error)
|
||||
showError("Failed to receive stock. Please try again.")
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="sm:max-w-lg">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Confirm Received Stock</DialogTitle>
|
||||
<DialogDescription>
|
||||
Confirm the quantities of items received. Discrepancies will be highlighted.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
|
||||
{fields.map((field, index) => {
|
||||
const orderedQty = field.orderedQuantity
|
||||
const receivedQty = form.getValues(`receivedItems.${index}.receivedQuantity`)
|
||||
const discrepancy = receivedQty !== orderedQty
|
||||
|
||||
return (
|
||||
<div key={field.id} className="flex items-center space-x-4">
|
||||
<input type="hidden" {...form.register(`receivedItems.${index}.stockItemId` as const)} />
|
||||
<div className="flex-1">
|
||||
<p className="font-semibold">{items[index].itemName}</p>
|
||||
<p className="text-sm text-muted-foreground">Ordered: {orderedQty}</p>
|
||||
</div>
|
||||
<div className="w-32">
|
||||
<label htmlFor={`receivedQuantity-${index}`} className="block text-sm font-medium text-gray-700">
|
||||
Received Qty
|
||||
</label>
|
||||
<input
|
||||
id={`receivedQuantity-${index}`}
|
||||
type="number"
|
||||
min={0}
|
||||
{...form.register(`receivedItems.${index}.receivedQuantity` as const, { valueAsNumber: true })}
|
||||
className={`mt-1 block w-full rounded-md border border-gray-300 px-2 py-1 shadow-sm focus:border-primary focus:ring-1 focus:ring-primary ${
|
||||
discrepancy ? "border-red-500" : ""
|
||||
}`}
|
||||
/>
|
||||
{discrepancy && (
|
||||
<p className="text-xs text-red-600">Discrepancy detected</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
<div className="flex justify-end space-x-2 pt-4">
|
||||
<Button variant="outline" onClick={() => onOpenChange(false)}>Cancel</Button>
|
||||
<Button type="submit">Confirm Received</Button>
|
||||
</div>
|
||||
</form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
|
|
@ -9,13 +9,13 @@ import {
|
|||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
import { columns, PurchaseOrder } from "@/components/purchase-orders/columns";
|
||||
import { columns } from "@/components/purchase-orders/columns";
|
||||
import { PurchaseOrderDataTable } from "@/components/purchase-orders/PurchaseOrderDataTable";
|
||||
import pb from "@/lib/pocketbase";
|
||||
|
||||
const PurchaseOrders = () => {
|
||||
const [purchaseOrderOpen, setPurchaseOrderOpen] = useState(false);
|
||||
const [purchaseOrders, setPurchaseOrders] = useState<PurchaseOrder[]>([]);
|
||||
const [purchaseOrders, setPurchaseOrders] = useState([]);
|
||||
const [refreshTrigger, setRefreshTrigger] = useState(0);
|
||||
const [filterValue, setFilterValue] = useState("");
|
||||
|
||||
|
|
@ -23,14 +23,21 @@ const PurchaseOrders = () => {
|
|||
try {
|
||||
const records = await pb.collection('purchase_orders').getList(1, 50, {
|
||||
sort: '-orderDate',
|
||||
expand: 'items',
|
||||
});
|
||||
|
||||
const formattedData: PurchaseOrder[] = records.items.map((record: any) => ({
|
||||
const formattedData = records.items.map((record) => ({
|
||||
id: record.id,
|
||||
supplierName: record.supplierName,
|
||||
orderDate: record.orderDate,
|
||||
status: record.status,
|
||||
itemCount: record.items?.length || 0,
|
||||
items: (record.expand?.items || []).map((item) => ({
|
||||
stockItemId: item.id,
|
||||
itemName: item.name,
|
||||
quantity: item.quantity,
|
||||
})),
|
||||
quantities: record.quantities || [],
|
||||
}));
|
||||
|
||||
setPurchaseOrders(formattedData);
|
||||
|
|
@ -74,6 +81,7 @@ const PurchaseOrders = () => {
|
|||
data={purchaseOrders}
|
||||
filterValue={filterValue}
|
||||
onFilterChange={setFilterValue}
|
||||
refreshData={handleRefreshData}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
Loading…
Reference in a new issue