form create added

This commit is contained in:
berkay 2024-12-19 14:35:10 +03:00
parent 44c458cd4c
commit 6720c69e6f
12 changed files with 1671 additions and 310 deletions

504
package-lock.json generated
View File

@ -12,14 +12,16 @@
"@radix-ui/react-avatar": "^1.1.1", "@radix-ui/react-avatar": "^1.1.1",
"@radix-ui/react-dialog": "^1.1.2", "@radix-ui/react-dialog": "^1.1.2",
"@radix-ui/react-label": "^2.1.0", "@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-popover": "^1.1.2", "@radix-ui/react-popover": "^1.1.4",
"@radix-ui/react-scroll-area": "^1.2.1", "@radix-ui/react-scroll-area": "^1.2.2",
"@radix-ui/react-slot": "^1.1.0", "@radix-ui/react-slot": "^1.1.1",
"@radix-ui/react-switch": "^1.1.1", "@radix-ui/react-switch": "^1.1.1",
"@radix-ui/react-toast": "^1.2.2", "@radix-ui/react-toast": "^1.2.2",
"chrono-node": "^2.7.7",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"cmdk": "^1.0.0", "cmdk": "^1.0.0",
"date-fns": "^3.6.0",
"flatpickr": "^4.6.13", "flatpickr": "^4.6.13",
"jsvectormap": "^1.6.0", "jsvectormap": "^1.6.0",
"lucide-react": "^0.462.0", "lucide-react": "^0.462.0",
@ -28,6 +30,7 @@
"next-themes": "^0.4.3", "next-themes": "^0.4.3",
"react": "^18.3.1", "react": "^18.3.1",
"react-apexcharts": "^1.6.0", "react-apexcharts": "^1.6.0",
"react-day-picker": "^8.10.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"react-hook-form": "^7.53.2", "react-hook-form": "^7.53.2",
"react-phone-number-input": "^3.4.9", "react-phone-number-input": "^3.4.9",
@ -685,11 +688,33 @@
"integrity": "sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==" "integrity": "sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA=="
}, },
"node_modules/@radix-ui/react-arrow": { "node_modules/@radix-ui/react-arrow": {
"version": "1.1.0", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.0.tgz", "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.1.tgz",
"integrity": "sha512-FmlW1rCg7hBpEBwFbjHwCW6AmWLQM6g/v0Sn8XbP9NvmSZ2San1FpQeyPtufzOMSIx7Y4dzjlHoifhp+7NkZhw==", "integrity": "sha512-NaVpZfmv8SKeZbn4ijN2V3jlHA9ngBG16VnIIm22nUR0Yk8KUALyBxT3KYEUnNuch9sTE8UTsS3whzBgKOL30w==",
"dependencies": { "dependencies": {
"@radix-ui/react-primitive": "2.0.0" "@radix-ui/react-primitive": "2.0.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-arrow/node_modules/@radix-ui/react-primitive": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.1.tgz",
"integrity": "sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==",
"dependencies": {
"@radix-ui/react-slot": "1.1.1"
}, },
"peerDependencies": { "peerDependencies": {
"@types/react": "*", "@types/react": "*",
@ -770,6 +795,23 @@
} }
} }
}, },
"node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-slot": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz",
"integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.0"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-compose-refs": { "node_modules/@radix-ui/react-compose-refs": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz", "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz",
@ -833,6 +875,23 @@
} }
} }
}, },
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-slot": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz",
"integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.0"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-direction": { "node_modules/@radix-ui/react-direction": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.0.tgz", "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.0.tgz",
@ -951,25 +1010,25 @@
} }
}, },
"node_modules/@radix-ui/react-popover": { "node_modules/@radix-ui/react-popover": {
"version": "1.1.2", "version": "1.1.4",
"resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.2.tgz", "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.4.tgz",
"integrity": "sha512-u2HRUyWW+lOiA2g0Le0tMmT55FGOEWHwPFt1EPfbLly7uXQExFo5duNKqG2DzmFXIdqOeNd+TpE8baHWJCyP9w==", "integrity": "sha512-aUACAkXx8LaFymDma+HQVji7WhvEhpFJ7+qPz17Nf4lLZqtreGOFRiNQWQmhzp7kEWg9cOyyQJpdIMUMPc/CPw==",
"dependencies": { "dependencies": {
"@radix-ui/primitive": "1.1.0", "@radix-ui/primitive": "1.1.1",
"@radix-ui/react-compose-refs": "1.1.0", "@radix-ui/react-compose-refs": "1.1.1",
"@radix-ui/react-context": "1.1.1", "@radix-ui/react-context": "1.1.1",
"@radix-ui/react-dismissable-layer": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.3",
"@radix-ui/react-focus-guards": "1.1.1", "@radix-ui/react-focus-guards": "1.1.1",
"@radix-ui/react-focus-scope": "1.1.0", "@radix-ui/react-focus-scope": "1.1.1",
"@radix-ui/react-id": "1.1.0", "@radix-ui/react-id": "1.1.0",
"@radix-ui/react-popper": "1.2.0", "@radix-ui/react-popper": "1.2.1",
"@radix-ui/react-portal": "1.1.2", "@radix-ui/react-portal": "1.1.3",
"@radix-ui/react-presence": "1.1.1", "@radix-ui/react-presence": "1.1.2",
"@radix-ui/react-primitive": "2.0.0", "@radix-ui/react-primitive": "2.0.1",
"@radix-ui/react-slot": "1.1.0", "@radix-ui/react-slot": "1.1.1",
"@radix-ui/react-use-controllable-state": "1.1.0", "@radix-ui/react-use-controllable-state": "1.1.0",
"aria-hidden": "^1.1.1", "aria-hidden": "^1.1.1",
"react-remove-scroll": "2.6.0" "react-remove-scroll": "^2.6.1"
}, },
"peerDependencies": { "peerDependencies": {
"@types/react": "*", "@types/react": "*",
@ -986,16 +1045,177 @@
} }
} }
}, },
"node_modules/@radix-ui/react-popover/node_modules/@radix-ui/primitive": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz",
"integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA=="
},
"node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-compose-refs": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz",
"integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==",
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-dismissable-layer": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.3.tgz",
"integrity": "sha512-onrWn/72lQoEucDmJnr8uczSNTujT0vJnA/X5+3AkChVPowr8n1yvIKIabhWyMQeMvvmdpsvcyDqx3X1LEXCPg==",
"dependencies": {
"@radix-ui/primitive": "1.1.1",
"@radix-ui/react-compose-refs": "1.1.1",
"@radix-ui/react-primitive": "2.0.1",
"@radix-ui/react-use-callback-ref": "1.1.0",
"@radix-ui/react-use-escape-keydown": "1.1.0"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-focus-scope": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.1.tgz",
"integrity": "sha512-01omzJAYRxXdG2/he/+xy+c8a8gCydoQ1yOxnWNcRhrrBW5W+RQJ22EK1SaO8tb3WoUsuEw7mJjBozPzihDFjA==",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.1",
"@radix-ui/react-primitive": "2.0.1",
"@radix-ui/react-use-callback-ref": "1.1.0"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-portal": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.3.tgz",
"integrity": "sha512-NciRqhXnGojhT93RPyDaMPfLH3ZSl4jjIFbZQ1b/vxvZEdHsBZ49wP9w8L3HzUQwep01LcWtkUvm0OVB5JAHTw==",
"dependencies": {
"@radix-ui/react-primitive": "2.0.1",
"@radix-ui/react-use-layout-effect": "1.1.0"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-presence": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.2.tgz",
"integrity": "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.1",
"@radix-ui/react-use-layout-effect": "1.1.0"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-primitive": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.1.tgz",
"integrity": "sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==",
"dependencies": {
"@radix-ui/react-slot": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-popover/node_modules/react-remove-scroll": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.2.tgz",
"integrity": "sha512-KmONPx5fnlXYJQqC62Q+lwIeAk64ws/cUw6omIumRzMRPqgnYqhSSti99nbj0Ry13bv7dF+BKn7NB+OqkdZGTw==",
"dependencies": {
"react-remove-scroll-bar": "^2.3.7",
"react-style-singleton": "^2.2.1",
"tslib": "^2.1.0",
"use-callback-ref": "^1.3.3",
"use-sidecar": "^1.1.2"
},
"engines": {
"node": ">=10"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-popper": { "node_modules/@radix-ui/react-popper": {
"version": "1.2.0", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.0.tgz", "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.1.tgz",
"integrity": "sha512-ZnRMshKF43aBxVWPWvbj21+7TQCvhuULWJ4gNIKYpRlQt5xGRhLx66tMp8pya2UkGHTSlhpXwmjqltDYHhw7Vg==", "integrity": "sha512-3kn5Me69L+jv82EKRuQCXdYyf1DqHwD2U/sxoNgBGCB7K9TRc3bQamQ+5EPM9EvyPdli0W41sROd+ZU1dTCztw==",
"dependencies": { "dependencies": {
"@floating-ui/react-dom": "^2.0.0", "@floating-ui/react-dom": "^2.0.0",
"@radix-ui/react-arrow": "1.1.0", "@radix-ui/react-arrow": "1.1.1",
"@radix-ui/react-compose-refs": "1.1.0", "@radix-ui/react-compose-refs": "1.1.1",
"@radix-ui/react-context": "1.1.0", "@radix-ui/react-context": "1.1.1",
"@radix-ui/react-primitive": "2.0.0", "@radix-ui/react-primitive": "2.0.1",
"@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-callback-ref": "1.1.0",
"@radix-ui/react-use-layout-effect": "1.1.0", "@radix-ui/react-use-layout-effect": "1.1.0",
"@radix-ui/react-use-rect": "1.1.0", "@radix-ui/react-use-rect": "1.1.0",
@ -1017,10 +1237,10 @@
} }
} }
}, },
"node_modules/@radix-ui/react-popper/node_modules/@radix-ui/react-context": { "node_modules/@radix-ui/react-popper/node_modules/@radix-ui/react-compose-refs": {
"version": "1.1.0", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.0.tgz", "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz",
"integrity": "sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==", "integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==",
"peerDependencies": { "peerDependencies": {
"@types/react": "*", "@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
@ -1031,6 +1251,28 @@
} }
} }
}, },
"node_modules/@radix-ui/react-popper/node_modules/@radix-ui/react-primitive": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.1.tgz",
"integrity": "sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==",
"dependencies": {
"@radix-ui/react-slot": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-portal": { "node_modules/@radix-ui/react-portal": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.2.tgz", "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.2.tgz",
@ -1099,18 +1341,35 @@
} }
} }
}, },
"node_modules/@radix-ui/react-primitive/node_modules/@radix-ui/react-slot": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz",
"integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.0"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-scroll-area": { "node_modules/@radix-ui/react-scroll-area": {
"version": "1.2.1", "version": "1.2.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.1.tgz", "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.2.tgz",
"integrity": "sha512-FnM1fHfCtEZ1JkyfH/1oMiTcFBQvHKl4vD9WnpwkLgtF+UmnXMCad6ECPTaAjcDjam+ndOEJWgHyKDGNteWSHw==", "integrity": "sha512-EFI1N/S3YxZEW/lJ/H1jY3njlvTd8tBmgKEn4GHi51+aMm94i6NmAJstsm5cu3yJwYqYc93gpCPm21FeAbFk6g==",
"dependencies": { "dependencies": {
"@radix-ui/number": "1.1.0", "@radix-ui/number": "1.1.0",
"@radix-ui/primitive": "1.1.0", "@radix-ui/primitive": "1.1.1",
"@radix-ui/react-compose-refs": "1.1.0", "@radix-ui/react-compose-refs": "1.1.1",
"@radix-ui/react-context": "1.1.1", "@radix-ui/react-context": "1.1.1",
"@radix-ui/react-direction": "1.1.0", "@radix-ui/react-direction": "1.1.0",
"@radix-ui/react-presence": "1.1.1", "@radix-ui/react-presence": "1.1.2",
"@radix-ui/react-primitive": "2.0.0", "@radix-ui/react-primitive": "2.0.1",
"@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-callback-ref": "1.1.0",
"@radix-ui/react-use-layout-effect": "1.1.0" "@radix-ui/react-use-layout-effect": "1.1.0"
}, },
@ -1129,13 +1388,91 @@
} }
} }
}, },
"node_modules/@radix-ui/react-slot": { "node_modules/@radix-ui/react-scroll-area/node_modules/@radix-ui/primitive": {
"version": "1.1.0", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz", "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz",
"integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==", "integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA=="
"dependencies": { },
"@radix-ui/react-compose-refs": "1.1.0" "node_modules/@radix-ui/react-scroll-area/node_modules/@radix-ui/react-compose-refs": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz",
"integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==",
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
}, },
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-scroll-area/node_modules/@radix-ui/react-presence": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.2.tgz",
"integrity": "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.1",
"@radix-ui/react-use-layout-effect": "1.1.0"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-scroll-area/node_modules/@radix-ui/react-primitive": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.1.tgz",
"integrity": "sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==",
"dependencies": {
"@radix-ui/react-slot": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-slot": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.1.tgz",
"integrity": "sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-slot/node_modules/@radix-ui/react-compose-refs": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz",
"integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==",
"peerDependencies": { "peerDependencies": {
"@types/react": "*", "@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
@ -1629,6 +1966,17 @@
"node": ">= 6" "node": ">= 6"
} }
}, },
"node_modules/chrono-node": {
"version": "2.7.7",
"resolved": "https://registry.npmjs.org/chrono-node/-/chrono-node-2.7.7.tgz",
"integrity": "sha512-p3S7gotuTPu5oqhRL2p1fLwQXGgdQaRTtWR3e8Di9P1Pa9mzkK5DWR5AWBieMUh2ZdOnPgrK+zCrbbtyuA+D/Q==",
"dependencies": {
"dayjs": "^1.10.0"
},
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
}
},
"node_modules/class-variance-authority": { "node_modules/class-variance-authority": {
"version": "0.7.1", "version": "0.7.1",
"resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz",
@ -1755,6 +2103,20 @@
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
"devOptional": true "devOptional": true
}, },
"node_modules/date-fns": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz",
"integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/kossnocorp"
}
},
"node_modules/dayjs": {
"version": "1.11.13",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz",
"integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg=="
},
"node_modules/detect-libc": { "node_modules/detect-libc": {
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz",
@ -1944,14 +2306,6 @@
} }
} }
}, },
"node_modules/invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
"dependencies": {
"loose-envify": "^1.0.0"
}
},
"node_modules/is-arrayish": { "node_modules/is-arrayish": {
"version": "0.3.2", "version": "0.3.2",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
@ -2548,6 +2902,19 @@
"react": ">=0.13" "react": ">=0.13"
} }
}, },
"node_modules/react-day-picker": {
"version": "8.10.1",
"resolved": "https://registry.npmjs.org/react-day-picker/-/react-day-picker-8.10.1.tgz",
"integrity": "sha512-TMx7fNbhLk15eqcMt+7Z7S2KF7mfTId/XJDjKE8f+IUcFn0l08/kI4FiYTL/0yuOLmEcbR4Fwe3GJf/NiiMnPA==",
"funding": {
"type": "individual",
"url": "https://github.com/sponsors/gpbl"
},
"peerDependencies": {
"date-fns": "^2.28.0 || ^3.0.0",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/react-dom": { "node_modules/react-dom": {
"version": "18.3.1", "version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
@ -2621,19 +2988,19 @@
} }
}, },
"node_modules/react-remove-scroll-bar": { "node_modules/react-remove-scroll-bar": {
"version": "2.3.6", "version": "2.3.8",
"resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.6.tgz", "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz",
"integrity": "sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==", "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==",
"dependencies": { "dependencies": {
"react-style-singleton": "^2.2.1", "react-style-singleton": "^2.2.2",
"tslib": "^2.0.0" "tslib": "^2.0.0"
}, },
"engines": { "engines": {
"node": ">=10" "node": ">=10"
}, },
"peerDependencies": { "peerDependencies": {
"@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", "@types/react": "*",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0" "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}, },
"peerDependenciesMeta": { "peerDependenciesMeta": {
"@types/react": { "@types/react": {
@ -2642,20 +3009,19 @@
} }
}, },
"node_modules/react-style-singleton": { "node_modules/react-style-singleton": {
"version": "2.2.1", "version": "2.2.3",
"resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz", "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz",
"integrity": "sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==", "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==",
"dependencies": { "dependencies": {
"get-nonce": "^1.0.0", "get-nonce": "^1.0.0",
"invariant": "^2.2.4",
"tslib": "^2.0.0" "tslib": "^2.0.0"
}, },
"engines": { "engines": {
"node": ">=10" "node": ">=10"
}, },
"peerDependencies": { "peerDependencies": {
"@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", "@types/react": "*",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0" "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
}, },
"peerDependenciesMeta": { "peerDependenciesMeta": {
"@types/react": { "@types/react": {
@ -3107,9 +3473,9 @@
"dev": true "dev": true
}, },
"node_modules/use-callback-ref": { "node_modules/use-callback-ref": {
"version": "1.3.2", "version": "1.3.3",
"resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.2.tgz", "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz",
"integrity": "sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA==", "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==",
"dependencies": { "dependencies": {
"tslib": "^2.0.0" "tslib": "^2.0.0"
}, },
@ -3117,8 +3483,8 @@
"node": ">=10" "node": ">=10"
}, },
"peerDependencies": { "peerDependencies": {
"@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", "@types/react": "*",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0" "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
}, },
"peerDependenciesMeta": { "peerDependenciesMeta": {
"@types/react": { "@types/react": {

View File

@ -13,14 +13,16 @@
"@radix-ui/react-avatar": "^1.1.1", "@radix-ui/react-avatar": "^1.1.1",
"@radix-ui/react-dialog": "^1.1.2", "@radix-ui/react-dialog": "^1.1.2",
"@radix-ui/react-label": "^2.1.0", "@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-popover": "^1.1.2", "@radix-ui/react-popover": "^1.1.4",
"@radix-ui/react-scroll-area": "^1.2.1", "@radix-ui/react-scroll-area": "^1.2.2",
"@radix-ui/react-slot": "^1.1.0", "@radix-ui/react-slot": "^1.1.1",
"@radix-ui/react-switch": "^1.1.1", "@radix-ui/react-switch": "^1.1.1",
"@radix-ui/react-toast": "^1.2.2", "@radix-ui/react-toast": "^1.2.2",
"chrono-node": "^2.7.7",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"cmdk": "^1.0.0", "cmdk": "^1.0.0",
"date-fns": "^3.6.0",
"flatpickr": "^4.6.13", "flatpickr": "^4.6.13",
"jsvectormap": "^1.6.0", "jsvectormap": "^1.6.0",
"lucide-react": "^0.462.0", "lucide-react": "^0.462.0",
@ -29,6 +31,7 @@
"next-themes": "^0.4.3", "next-themes": "^0.4.3",
"react": "^18.3.1", "react": "^18.3.1",
"react-apexcharts": "^1.6.0", "react-apexcharts": "^1.6.0",
"react-day-picker": "^8.10.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"react-hook-form": "^7.53.2", "react-hook-form": "^7.53.2",
"react-phone-number-input": "^3.4.9", "react-phone-number-input": "^3.4.9",

View File

@ -88,11 +88,6 @@ const Build: React.FC = () => {
...prev, ...prev,
[key]: component.component, [key]: component.component,
})); }));
} else {
setEndpointNeedsList((prev) => ({
...prev,
[key]: component.notAllowed,
}));
} }
} }
}) })

View File

@ -14,6 +14,7 @@ import * as z from "zod";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { SmartDatetimeInput } from "@/components/ui/smart-datetime-input";
interface FormPageInterface { interface FormPageInterface {
validatedData: any; validatedData: any;
@ -36,20 +37,16 @@ const FormPage: React.FC<FormPageInterface> = ({
...validatedData, ...validatedData,
}, },
}); });
function submitUpdate(formData: z.infer<typeof validSchemaZod>) {
function submitUpdate(formData: FormData) { let newDataForm: any = {};
let newFormData: any = {}; Object.entries(formData).map(([key, value]) => {
Object.entries(Object.fromEntries(formData)).forEach(([key, value]) => { if (typeof value === "number" && value !== 0) {
if (apiValidation[key].fieldType === "integer") { newDataForm[key] = value;
const newNumber = typeof value === "string" ? 0 : Number(value); } else if (typeof value === "string" && value !== "") {
newFormData[key] = newNumber; newDataForm[key] = value;
} else {
newFormData[key] = value;
} }
}); });
const validated = validSchemaZod.safeParse(newFormData); console.log(newDataForm);
console.log("validated", validated);
console.log("validated", validated.error);
} }
return ( return (
@ -58,8 +55,7 @@ const FormPage: React.FC<FormPageInterface> = ({
<div> <div>
<Form {...form}> <Form {...form}>
<form <form
action={submitUpdate} onSubmit={form.handleSubmit(submitUpdate)}
// onSubmit={form.handleSubmit(submitUpdate)}
className="space-y-5 max-w-3xl mx-auto py-10" className="space-y-5 max-w-3xl mx-auto py-10"
> >
<div className=" absolute w-[80px] right-20 bg-emerald-700"> <div className=" absolute w-[80px] right-20 bg-emerald-700">
@ -89,88 +85,96 @@ const FormPage: React.FC<FormPageInterface> = ({
if (apiValidation[key]?.fieldType === "string") { if (apiValidation[key]?.fieldType === "string") {
return ( return (
<div className="mb-4" key={`${key}-header`}> <div className="mb-4" key={`${key}-header`}>
<label className="mb-2.5 block font-medium text-black dark:text-white">
{apiHeaders[key]}
</label>
<div className="relative" key={key}> <div className="relative" key={key}>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="absolute right-4 top-1/2 lucide lucide-pencil"
>
<path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z" />
<path d="m15 5 4 4" />
</svg>
<FormField <FormField
control={form.control} control={form.control}
name={key as keyof z.infer<typeof zodValidation>} name={String(key)}
render={({ field }) => ( render={({ field }) => {
<FormItem> return (
<FormControl> <FormItem>
<input <FormLabel>{apiHeaders[key]}</FormLabel>
type="text" <FormControl>
className="w-full rounded-lg border border-stroke bg-transparent py-4 pl-6 pr-10 text-black outline-none focus:border-primary focus-visible:shadow-none dark:border-form-strokedark dark:bg-form-input dark:text-white dark:focus:border-primary" <SmartDatetimeInput
{...field} value={field.value}
value={field.value} onValueChange={field.onChange}
/> placeholder="e.g. Tomorrow morning 9am"
</FormControl> />
<FormMessage /> </FormControl>
</FormItem> {String(form.formState.errors[key]?.type) ===
)} "invalid_type" ? (
<span id={key} className="text-red-700">
"Lütfen sayısal bir değer giriniz"
</span>
) : (
<></>
)}
</FormItem>
);
}}
/> />
<span className="absolute right-4 top-4">
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="lucide lucide-pencil"
>
<path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z" />
<path d="m15 5 4 4" />
</svg>
</span>
</div> </div>
</div> </div>
); );
} else if (apiValidation[key]?.fieldType === "integer") { } else if (apiValidation[key]?.fieldType === "integer") {
return ( return (
<div className="mb-4" key={`${key}-header`}> <div className="mb-4" key={`${key}-header`}>
<label className="mb-2.5 block font-medium text-black dark:text-white">
{apiHeaders[key]}
</label>
<div className="relative" key={key}> <div className="relative" key={key}>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="absolute right-4 top-1/2 lucide lucide-pencil"
>
<path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z" />
<path d="m15 5 4 4" />
</svg>
<FormField <FormField
control={form.control} control={form.control}
name={key as keyof z.infer<typeof zodValidation>} name={String(key)}
render={({ field }) => ( render={({ field }) => {
<FormItem> return (
<FormControl> <FormItem>
<input <FormLabel>{apiHeaders[key]}</FormLabel>
type="number" <FormControl>
className="w-full rounded-lg border border-stroke bg-transparent py-4 pl-6 pr-10 text-black outline-none focus:border-primary focus-visible:shadow-none dark:border-form-strokedark dark:bg-form-input dark:text-white dark:focus:border-primary" <Input
{...field} className="w-full rounded-lg border border-stroke bg-transparent py-4 pl-6 pr-10 text-black outline-none focus:border-primary focus-visible:shadow-none dark:border-form-strokedark dark:bg-form-input dark:text-white dark:focus:border-primary"
value={field.value || ""} {...field}
/> value={field.value || ""}
</FormControl> />
<FormMessage /> </FormControl>
</FormItem> {String(form.formState.errors[key]?.type) ===
)} "invalid_type" ? (
<span id={key} className="text-red-700">
"Lütfen sayısal bir değer giriniz"
</span>
) : (
<></>
)}
</FormItem>
);
}}
/> />
<span className="absolute right-4 top-4">
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="lucide lucide-pencil"
>
<path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z" />
<path d="m15 5 4 4" />
</svg>
</span>
</div> </div>
</div> </div>
); );

View File

@ -0,0 +1,186 @@
"use client";
import React from "react";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import * as z from "zod";
import { Input } from "@/components/ui/input";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { SmartDatetimeInput } from "@/components/ui/smart-datetime-input";
interface FormPageValidInterface {
zodValidation: any;
apiValidation: any;
apiHeaders: any;
}
const FormPageValid: React.FC<FormPageValidInterface> = ({
zodValidation,
apiValidation,
apiHeaders,
}) => {
const validSchemaZod = z.object({ ...zodValidation });
const form = useForm<z.infer<typeof validSchemaZod>>({
resolver: zodResolver(validSchemaZod),
});
function submitUpdate(formData: z.infer<typeof validSchemaZod>) {
let newDataForm: any = {};
Object.entries(formData).map(([key, value]) => {
if (typeof value === "number" && value !== 0) {
newDataForm[key] = value;
} else if (typeof value === "string" && value !== "") {
newDataForm[key] = value;
}
});
console.log(newDataForm);
}
return (
<>
{
<div>
<Form {...form}>
<form
onSubmit={form.handleSubmit(submitUpdate)}
className="space-y-5 max-w-3xl mx-auto py-10"
>
<div className=" absolute w-[80px] right-20 bg-emerald-700">
<span>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="lucide lucide-check"
>
<path d="M20 6 9 17l-5-5" />
</svg>
</span>
<input
type="submit"
value="Kaydet"
className="w-full cursor-pointer rounded-lg border p-4 text-white transition hover:bg-opacity-90"
/>
</div>
{Object.keys(zodValidation).map((key: string) => {
if (apiValidation[key]?.fieldType === "string") {
return (
<div className="mb-4" key={`${key}-header`}>
<div className="relative" key={key}>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="absolute right-4 top-1/2 lucide lucide-pencil"
>
<path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z" />
<path d="m15 5 4 4" />
</svg>
<FormField
control={form.control}
name={String(key)}
render={({ field }) => {
return (
<FormItem>
<FormLabel>{apiHeaders[key]}</FormLabel>
<FormControl>
<SmartDatetimeInput
value={field.value}
onValueChange={field.onChange}
placeholder="e.g. Tomorrow morning 9am"
/>
</FormControl>
{String(form.formState.errors[key]?.type) ===
"invalid_type" ? (
<span id={key} className="text-red-700">
"Lütfen sayısal bir değer giriniz"
</span>
) : (
<></>
)}
</FormItem>
);
}}
/>
</div>
</div>
);
} else if (apiValidation[key]?.fieldType === "integer") {
return (
<div className="mb-4" key={`${key}-header`}>
<div className="relative" key={key}>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="absolute right-4 top-1/2 lucide lucide-pencil"
>
<path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z" />
<path d="m15 5 4 4" />
</svg>
<FormField
control={form.control}
name={String(key)}
render={({ field }) => {
return (
<FormItem>
<FormLabel>{apiHeaders[key]}</FormLabel>
<FormControl>
<Input
className="w-full rounded-lg border border-stroke bg-transparent py-4 pl-6 pr-10 text-black outline-none focus:border-primary focus-visible:shadow-none dark:border-form-strokedark dark:bg-form-input dark:text-white dark:focus:border-primary"
{...field}
value={field.value || ""}
/>
</FormControl>
{String(form.formState.errors[key]?.type) ===
"invalid_type" ? (
<span id={key} className="text-red-700">
"Lütfen sayısal bir değer giriniz"
</span>
) : (
<></>
)}
</FormItem>
);
}}
/>
</div>
</div>
);
}
})}
</form>
</Form>
</div>
}
</>
);
};
export default FormPageValid;

View File

@ -15,19 +15,22 @@ import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { retrieveValidationsByEndpoint } from "../functions/retrieveEndpointAndValidations"; import { retrieveValidationsByEndpoint } from "../functions/retrieveEndpointAndValidations";
import FormPageValid from "./FormPageValid";
interface CreatePageProps { interface CreatePageProps {
pageInfo: any; pageInfo: any;
endpoint: string; endpoint: string;
returnToPage: any; returnToPage: any;
saveFunction: any;
} }
const CreatePage: React.FC<CreatePageProps> = ({ const CreatePage: React.FC<CreatePageProps> = ({
pageInfo, pageInfo,
endpoint, endpoint,
returnToPage, returnToPage,
saveFunction,
}) => { }) => {
const [zodValidation, setZodValidation] = React.useState(z.object({})); const [zodValidation, setZodValidation] = React.useState(null);
const [apiValidation, setApiValidation] = React.useState<{ const [apiValidation, setApiValidation] = React.useState<{
[key: string]: any; [key: string]: any;
}>({}); }>({});
@ -37,18 +40,13 @@ const CreatePage: React.FC<CreatePageProps> = ({
React.useEffect(() => { React.useEffect(() => {
retrieveValidationsByEndpoint(endpoint).then((validations: any) => { retrieveValidationsByEndpoint(endpoint).then((validations: any) => {
setZodValidation(validations.zodValidation as any); setZodValidation(validations.zodValidation);
setApiHeaders(validations.apiValidation?.headers as Object); setApiValidation(validations.apiValidation?.validated);
setApiValidation(validations.apiValidation?.validated as Object); setApiHeaders(validations.apiValidation?.headers);
}); });
}, []); }, []);
const form = useForm<z.infer<typeof zodValidation>>({
resolver: zodResolver(zodValidation),
});
function closeFormPage() {}
function saveAction() {}
return ( return (
<div> <div>
@ -77,135 +75,14 @@ const CreatePage: React.FC<CreatePageProps> = ({
</svg> </svg>
} }
/> />
<div className="absolute right-0">
<EventButton
onClick={() => saveAction()}
label="Kaydet"
bgColor="bg-emerald-700"
icon={
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="lucide lucide-check"
>
<path d="M20 6 9 17l-5-5" />
</svg>
}
/>
</div>
</div> </div>
{ {zodValidation && apiValidation && (
<Form {...form}> <FormPageValid
<form zodValidation={zodValidation}
onSubmit={form.handleSubmit()} apiValidation={apiValidation}
className="space-y-5 max-w-3xl mx-auto py-10" apiHeaders={apiHeaders}
> />
{Object.keys(apiValidation).map((key: string) => { )}
const keyValidation = apiValidation[key] || {
fieldType: { type: "string" },
required: false,
};
const fieldType = keyValidation.fieldType?.type || "string";
if (fieldType === "string" && apiValidation[key]) {
return (
<div className="mb-4" key={`${key}-header`}>
<label className="mb-2.5 block font-medium text-black dark:text-white">
{apiHeaders[key]}
</label>
<div className="relative" key={key}>
<FormField
control={form.control}
name={key as keyof z.infer<typeof zodValidation>}
render={({ field }) => (
<FormItem>
<FormControl>
<input
type="text"
className="w-full rounded-lg border border-stroke bg-transparent py-4 pl-6 pr-10 text-black outline-none focus:border-primary focus-visible:shadow-none dark:border-form-strokedark dark:bg-form-input dark:text-white dark:focus:border-primary"
{...field}
value={field.value}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<span className="absolute right-4 top-4">
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="lucide lucide-pencil"
>
<path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z" />
<path d="m15 5 4 4" />
</svg>
</span>
</div>
</div>
);
} else if (fieldType === "integer" && apiValidation[key]) {
return (
<div className="mb-4" key={`${key}-header`}>
<label className="mb-2.5 block font-medium text-black dark:text-white">
{apiHeaders[key]}
</label>
<div className="relative" key={key}>
<FormField
control={form.control}
name={key as keyof z.infer<typeof zodValidation>}
render={({ field }) => (
<FormItem>
<FormControl>
<input
type="text"
className="w-full rounded-lg border border-stroke bg-transparent py-4 pl-6 pr-10 text-black outline-none focus:border-primary focus-visible:shadow-none dark:border-form-strokedark dark:bg-form-input dark:text-white dark:focus:border-primary"
{...field}
value={field.value}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<span className="absolute right-4 top-4">
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="lucide lucide-pencil"
>
<path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z" />
<path d="m15 5 4 4" />
</svg>
</span>
</div>
</div>
);
}
})}
</form>
</Form>
}
</div> </div>
); );
}; };

View File

@ -3,7 +3,6 @@ import React from "react";
import EventButton from "./ButtonEvent"; import EventButton from "./ButtonEvent";
import FormPage from "./FormPage"; import FormPage from "./FormPage";
import * as z from "zod";
import { retrieveValidationsByEndpointWithData } from "../functions/retrieveEndpointAndValidations"; import { retrieveValidationsByEndpointWithData } from "../functions/retrieveEndpointAndValidations";
interface UpdatePageButtonProps { interface UpdatePageButtonProps {

View File

@ -4,6 +4,7 @@ import {
retrieveHeadersAndValidationByEndpoint, retrieveHeadersAndValidationByEndpoint,
retrieveHeadersEndpoint, retrieveHeadersEndpoint,
} from "@/(apicalls)/validations/validations"; } from "@/(apicalls)/validations/validations";
import { ZodDecimal } from "@/components/zodDecimal";
async function retrieveValidationsByEndpoint(endpoint: string) { async function retrieveValidationsByEndpoint(endpoint: string) {
let apiValidation: any = {}; let apiValidation: any = {};
@ -97,11 +98,8 @@ async function retrieveValidationsByEndpointWithData(
.refine((val) => val !== "" || val !== null); .refine((val) => val !== "" || val !== null);
} else if (fieldType === "integer") { } else if (fieldType === "integer") {
zodValidation[key] = required zodValidation[key] = required
? z.number().transform((val) => Number(val)) ? ZodDecimal.create({ coerce: true })
: z : ZodDecimal.create({ coerce: true }).optional();
.number()
.optional()
.transform((val) => Number(val));
} }
} }
}); });

View File

@ -0,0 +1,76 @@
"use client"
import * as React from "react"
import { ChevronLeft, ChevronRight } from "lucide-react"
import { DayPicker } from "react-day-picker"
import { cn } from "@/lib/utils"
import { buttonVariants } from "@/components/ui/button"
export type CalendarProps = React.ComponentProps<typeof DayPicker>
function Calendar({
className,
classNames,
showOutsideDays = true,
...props
}: CalendarProps) {
return (
<DayPicker
showOutsideDays={showOutsideDays}
className={cn("p-3", className)}
classNames={{
months: "flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0",
month: "space-y-4",
caption: "flex justify-center pt-1 relative items-center",
caption_label: "text-sm font-medium",
nav: "space-x-1 flex items-center",
nav_button: cn(
buttonVariants({ variant: "outline" }),
"h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100"
),
nav_button_previous: "absolute left-1",
nav_button_next: "absolute right-1",
table: "w-full border-collapse space-y-1",
head_row: "flex",
head_cell:
"text-muted-foreground rounded-md w-8 font-normal text-[0.8rem]",
row: "flex w-full mt-2",
cell: cn(
"relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-accent [&:has([aria-selected].day-outside)]:bg-accent/50 [&:has([aria-selected].day-range-end)]:rounded-r-md",
props.mode === "range"
? "[&:has(>.day-range-end)]:rounded-r-md [&:has(>.day-range-start)]:rounded-l-md first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md"
: "[&:has([aria-selected])]:rounded-md"
),
day: cn(
buttonVariants({ variant: "ghost" }),
"h-8 w-8 p-0 font-normal aria-selected:opacity-100"
),
day_range_start: "day-range-start",
day_range_end: "day-range-end",
day_selected:
"bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground",
day_today: "bg-accent text-accent-foreground",
day_outside:
"day-outside text-muted-foreground aria-selected:bg-accent/50 aria-selected:text-muted-foreground",
day_disabled: "text-muted-foreground opacity-50",
day_range_middle:
"aria-selected:bg-accent aria-selected:text-accent-foreground",
day_hidden: "invisible",
...classNames,
}}
components={{
IconLeft: ({ className, ...props }) => (
<ChevronLeft className={cn("h-4 w-4", className)} {...props} />
),
IconRight: ({ className, ...props }) => (
<ChevronRight className={cn("h-4 w-4", className)} {...props} />
),
}}
{...props}
/>
)
}
Calendar.displayName = "Calendar"
export { Calendar }

View File

@ -1,6 +1,6 @@
import * as React from "react"; import * as React from "react"
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils"
const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>( const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>(
({ className, type, ...props }, ref) => { ({ className, type, ...props }, ref) => {
@ -8,15 +8,15 @@ const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>(
<input <input
type={type} type={type}
className={cn( className={cn(
"flex h-14 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm", "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
className className
)} )}
ref={ref} ref={ref}
{...props} {...props}
/> />
); )
} }
); )
Input.displayName = "Input"; Input.displayName = "Input"
export { Input }; export { Input }

View File

@ -0,0 +1,551 @@
"use client";
import React from "react";
import { parseDate } from "chrono-node";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { ActiveModifiers } from "react-day-picker";
import { Calendar, CalendarProps } from "@/components/ui/calendar";
import { Input } from "@/components/ui/input";
import { Button, buttonVariants } from "@/components/ui/button";
import { cn } from "@/lib/utils";
import { Calendar as CalendarIcon, LucideTextCursorInput } from "lucide-react";
import { ScrollArea } from "@/components/ui/scroll-area";
import { format } from "date-fns";
/* -------------------------------------------------------------------------- */
/* Inspired By: */
/* @steventey */
/* ------------------https://dub.co/blog/smart-datetime-picker--------------- */
/* -------------------------------------------------------------------------- */
/**
* Utility function that parses dates.
* Parses a given date string using the `chrono-node` library.
*
* @param str - A string representation of a date and time.
* @returns A `Date` object representing the parsed date and time, or `null` if the string could not be parsed.
*/
export const parseDateTime = (str: Date | string) => {
if (str instanceof Date) return str;
return parseDate(str);
};
/**
* Converts a given timestamp or the current date and time to a string representation in the local time zone.
* format: `HH:mm`, adjusted for the local time zone.
*
* @param timestamp {Date | string}
* @returns A string representation of the timestamp
*/
export const getDateTimeLocal = (timestamp?: Date): string => {
const d = timestamp ? new Date(timestamp) : new Date();
if (d.toString() === "Invalid Date") return "";
return new Date(d.getTime() - d.getTimezoneOffset() * 60000)
.toISOString()
.split(":")
.slice(0, 2)
.join(":");
};
/**
* Formats a given date and time object or string into a human-readable string representation.
* "MMM D, YYYY h:mm A" (e.g. "Jan 1, 2023 12:00 PM").
*
* @param datetime - {Date | string}
* @returns A string representation of the date and time
*/
export const formatDateTime = (datetime: Date | string) => {
return new Date(datetime).toLocaleTimeString("en-US", {
month: "short",
day: "numeric",
year: "numeric",
hour: "numeric",
minute: "numeric",
hour12: true,
});
};
const inputBase =
"bg-transparent focus:outline-none focus:ring-0 focus-within:outline-none focus-within:ring-0 sm:text-sm disabled:cursor-not-allowed disabled:opacity-50";
// @source: https://www.perplexity.ai/search/in-javascript-how-RfI7fMtITxKr5c.V9Lv5KA#1
// use this pattern to validate the transformed date string for the natural language input
const naturalInputValidationPattern =
"^[A-Z][a-z]{2}sd{1,2},sd{4},sd{1,2}:d{2}s[AP]M$";
const DEFAULT_SIZE = 96;
/**
* Smart time input Docs: {@link: https://shadcn-extension.vercel.app/docs/smart-time-input}
*/
interface SmartDatetimeInputProps {
value?: Date;
onValueChange: (date: Date) => void;
}
interface SmartDatetimeInputContextProps extends SmartDatetimeInputProps {
Time: string;
onTimeChange: (time: string) => void;
}
const SmartDatetimeInputContext =
React.createContext<SmartDatetimeInputContextProps | null>(null);
const useSmartDateInput = () => {
const context = React.useContext(SmartDatetimeInputContext);
if (!context) {
throw new Error(
"useSmartDateInput must be used within SmartDateInputProvider",
);
}
return context;
};
export const SmartDatetimeInput = React.forwardRef<
HTMLInputElement,
Omit<
React.InputHTMLAttributes<HTMLInputElement>,
"type" | "ref" | "value" | "defaultValue" | "onBlur"
> &
SmartDatetimeInputProps
>(({ className, value, onValueChange, placeholder, disabled }, ref) => {
// ? refactor to be only used with controlled input
/* const [dateTime, setDateTime] = React.useState<Date | undefined>(
value ?? undefined
); */
const [Time, setTime] = React.useState<string>("");
const onTimeChange = React.useCallback((time: string) => {
setTime(time);
}, []);
return (
<SmartDatetimeInputContext.Provider
value={{ value, onValueChange, Time, onTimeChange }}
>
<div className="flex items-center justify-center">
<div
className={cn(
"flex gap-1 w-full p-1 items-center justify-between rounded-md border transition-all",
"focus-within:outline-0 focus:outline-0 focus:ring-0",
"placeholder:text-muted-foreground focus-visible:outline-0 ",
className,
)}
>
<DateTimeLocalInput />
<NaturalLanguageInput
placeholder={placeholder}
disabled={disabled}
ref={ref}
/>
</div>
</div>
</SmartDatetimeInputContext.Provider>
);
});
SmartDatetimeInput.displayName = "DatetimeInput";
// Make it a standalone component
const TimePicker = () => {
const { value, onValueChange, Time, onTimeChange } = useSmartDateInput();
const [activeIndex, setActiveIndex] = React.useState(-1);
const timestamp = 15;
const formateSelectedTime = React.useCallback(
(time: string, hour: number, partStamp: number) => {
onTimeChange(time);
const newVal = parseDateTime(value ?? new Date());
if (!newVal) return;
newVal.setHours(
hour,
partStamp === 0 ? parseInt("00") : timestamp * partStamp,
);
// ? refactor needed check if we want to use the new date
onValueChange(newVal);
},
[value],
);
const handleKeydown = React.useCallback(
(e: React.KeyboardEvent<HTMLDivElement>) => {
e.stopPropagation();
if (!document) return;
const moveNext = () => {
const nextIndex =
activeIndex + 1 > DEFAULT_SIZE - 1 ? 0 : activeIndex + 1;
const currentElm = document.getElementById(`time-${nextIndex}`);
currentElm?.focus();
setActiveIndex(nextIndex);
};
const movePrev = () => {
const prevIndex =
activeIndex - 1 < 0 ? DEFAULT_SIZE - 1 : activeIndex - 1;
const currentElm = document.getElementById(`time-${prevIndex}`);
currentElm?.focus();
setActiveIndex(prevIndex);
};
const setElement = () => {
const currentElm = document.getElementById(`time-${activeIndex}`);
if (!currentElm) return;
currentElm.focus();
const timeValue = currentElm.textContent ?? "";
// this should work now haha that hour is what does the trick
const PM_AM = timeValue.split(" ")[1];
const PM_AM_hour = parseInt(timeValue.split(" ")[0].split(":")[0]);
const hour =
PM_AM === "AM"
? PM_AM_hour === 12
? 0
: PM_AM_hour
: PM_AM_hour === 12
? 12
: PM_AM_hour + 12;
const part = Math.floor(
parseInt(timeValue.split(" ")[0].split(":")[1]) / 15,
);
formateSelectedTime(timeValue, hour, part);
};
const reset = () => {
const currentElm = document.getElementById(`time-${activeIndex}`);
currentElm?.blur();
setActiveIndex(-1);
};
switch (e.key) {
case "ArrowUp":
movePrev();
break;
case "ArrowDown":
moveNext();
break;
case "Escape":
reset();
break;
case "Enter":
setElement();
break;
}
},
[activeIndex, formateSelectedTime],
);
const handleClick = React.useCallback(
(hour: number, part: number, PM_AM: string, currentIndex: number) => {
formateSelectedTime(
`${hour}:${part === 0 ? "00" : timestamp * part} ${PM_AM}`,
hour,
part,
);
setActiveIndex(currentIndex);
},
[formateSelectedTime],
);
const currentTime = React.useMemo(() => {
const timeVal = Time.split(" ")[0];
return {
hours: parseInt(timeVal.split(":")[0]),
minutes: parseInt(timeVal.split(":")[1]),
};
}, [Time]);
React.useEffect(() => {
const getCurrentElementTime = () => {
const timeVal = Time.split(" ")[0];
const hours = parseInt(timeVal.split(":")[0]);
const minutes = parseInt(timeVal.split(":")[1]);
const PM_AM = Time.split(" ")[1];
const formatIndex =
PM_AM === "AM" ? hours : hours === 12 ? hours : hours + 12;
const formattedHours = formatIndex;
console.log(formatIndex);
for (let j = 0; j <= 3; j++) {
const diff = Math.abs(j * timestamp - minutes);
const selected =
PM_AM === (formattedHours >= 12 ? "PM" : "AM") &&
(minutes <= 53 ? diff < Math.ceil(timestamp / 2) : diff < timestamp);
if (selected) {
const trueIndex =
activeIndex === -1 ? formattedHours * 4 + j : activeIndex;
setActiveIndex(trueIndex);
const currentElm = document.getElementById(`time-${trueIndex}`);
currentElm?.scrollIntoView({
block: "center",
behavior: "smooth",
});
}
}
};
getCurrentElementTime();
}, [Time, activeIndex]);
const height = React.useMemo(() => {
if (!document) return;
const calendarElm = document.getElementById("calendar");
if (!calendarElm) return;
return calendarElm.style.height;
}, []);
return (
<div className="space-y-2 pr-3 py-3 relative ">
<h3 className="text-sm font-medium ">Time</h3>
<ScrollArea
onKeyDown={handleKeydown}
className="h-[90%] w-full focus-visible:outline-0 focus-visible:ring-0 focus-visible:ring-offset-0 focus-visible:border-0 py-0.5"
style={{
height,
}}
>
<ul
className={cn(
"flex items-center flex-col gap-1 h-full max-h-56 w-28 px-1 py-0.5",
)}
>
{Array.from({ length: 24 }).map((_, i) => {
const PM_AM = i >= 12 ? "PM" : "AM";
const formatIndex = i > 12 ? i % 12 : i === 0 || i === 12 ? 12 : i;
return Array.from({ length: 4 }).map((_, part) => {
const diff = Math.abs(part * timestamp - currentTime.minutes);
const trueIndex = i * 4 + part;
// ? refactor : add the select of the default time on the current device (H:MM)
const isSelected =
(currentTime.hours === i ||
currentTime.hours === formatIndex) &&
Time.split(" ")[1] === PM_AM &&
(currentTime.minutes <= 53
? diff < Math.ceil(timestamp / 2)
: diff < timestamp);
const isSuggested = !value && isSelected;
const currentValue = `${formatIndex}:${
part === 0 ? "00" : timestamp * part
} ${PM_AM}`;
return (
<li
tabIndex={isSelected ? 0 : -1}
id={`time-${trueIndex}`}
key={`time-${trueIndex}`}
aria-label="currentTime"
className={cn(
buttonVariants({
variant: isSuggested
? "secondary"
: isSelected
? "default"
: "outline",
}),
"h-8 px-3 w-full text-sm focus-visible:outline-0 outline-0 focus-visible:border-0 cursor-default ring-0",
)}
onClick={() => handleClick(i, part, PM_AM, trueIndex)}
onFocus={() => isSuggested && setActiveIndex(trueIndex)}
>
{currentValue}
</li>
);
});
})}
</ul>
</ScrollArea>
</div>
);
};
const NaturalLanguageInput = React.forwardRef<
HTMLInputElement,
{
placeholder?: string;
disabled?: boolean;
}
>(({ placeholder, ...props }, ref) => {
const { value, onValueChange, Time, onTimeChange } = useSmartDateInput();
const _placeholder = placeholder ?? 'e.g. "tomorrow at 5pm" or "in 2 hours"';
const [inputValue, setInputValue] = React.useState<string>("");
React.useEffect(() => {
const hour = new Date().getHours();
const timeVal = `${
hour >= 12 ? hour % 12 : hour
}:${new Date().getMinutes()} ${hour >= 12 ? "PM" : "AM"}`;
setInputValue(value ? formatDateTime(value) : "");
onTimeChange(value ? Time : timeVal);
}, [value, Time]);
const handleParse = React.useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
// parse the date string when the input field loses focus
const parsedDateTime = parseDateTime(e.currentTarget.value);
if (parsedDateTime) {
const PM_AM = parsedDateTime.getHours() >= 12 ? "PM" : "AM";
//fix the time format for this value
const PM_AM_hour = parsedDateTime.getHours();
const hour =
PM_AM_hour > 12
? PM_AM_hour % 12
: PM_AM_hour === 0 || PM_AM_hour === 12
? 12
: PM_AM_hour;
onValueChange(parsedDateTime);
setInputValue(formatDateTime(parsedDateTime));
onTimeChange(`${hour}:${parsedDateTime.getMinutes()} ${PM_AM}`);
}
},
[value],
);
const handleKeydown = React.useCallback(
(e: React.KeyboardEvent<HTMLInputElement>) => {
switch (e.key) {
case "Enter":
const parsedDateTime = parseDateTime(e.currentTarget.value);
if (parsedDateTime) {
const PM_AM = parsedDateTime.getHours() >= 12 ? "PM" : "AM";
//fix the time format for this value
const PM_AM_hour = parsedDateTime.getHours();
const hour =
PM_AM_hour > 12
? PM_AM_hour % 12
: PM_AM_hour === 0 || PM_AM_hour === 12
? 12
: PM_AM_hour;
onValueChange(parsedDateTime);
setInputValue(formatDateTime(parsedDateTime));
onTimeChange(`${hour}:${parsedDateTime.getMinutes()} ${PM_AM}`);
}
break;
}
},
[value],
);
return (
<Input
ref={ref}
type="text"
placeholder={_placeholder}
value={inputValue}
onChange={(e) => setInputValue(e.currentTarget.value)}
onKeyDown={handleKeydown}
onBlur={handleParse}
className={cn("px-2 mr-0.5 flex-1 border-none h-8 rounded", inputBase)}
{...props}
/>
);
});
NaturalLanguageInput.displayName = "NaturalLanguageInput";
type DateTimeLocalInputProps = {} & CalendarProps;
const DateTimeLocalInput = ({
className,
...props
}: DateTimeLocalInputProps) => {
const { value, onValueChange, Time } = useSmartDateInput();
const formateSelectedDate = React.useCallback(
(
date: Date | undefined,
selectedDate: Date,
m: ActiveModifiers,
e: React.MouseEvent,
) => {
const parsedDateTime = parseDateTime(selectedDate);
if (parsedDateTime) {
parsedDateTime.setHours(
parseInt(Time.split(":")[0]),
parseInt(Time.split(":")[1]),
);
onValueChange(parsedDateTime);
}
},
[value, Time],
);
return (
<Popover>
<PopoverTrigger asChild>
<Button
variant={"outline"}
size={"icon"}
className={cn(
"size-9 flex items-center justify-center font-normal",
!value && "text-muted-foreground",
)}
>
<CalendarIcon className="size-4" />
<span className="sr-only">calender</span>
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" sideOffset={8}>
<div className="flex gap-1">
<Calendar
{...props}
id={"calendar"}
className={cn("peer flex justify-end", inputBase, className)}
mode="single"
selected={value}
onSelect={formateSelectedDate}
initialFocus
/>
<TimePicker />
</div>
</PopoverContent>
</Popover>
);
};
DateTimeLocalInput.displayName = "DateTimeLocalInput";

View File

@ -0,0 +1,306 @@
import {
INVALID,
ParseContext,
ParseInput,
ParseReturnType,
ParseStatus,
RawCreateParams,
ZodIssueCode,
ZodParsedType,
ZodType,
ZodTypeDef,
addIssueToContext,
} from "zod";
export type ZodDecimalCheck =
| { kind: "precision"; value: number; message?: string }
| { kind: "wholeNumber"; value: number; message?: string }
| { kind: "min"; value: number; inclusive: boolean; message?: string }
| { kind: "max"; value: number; inclusive: boolean; message?: string }
| { kind: "finite"; message?: string };
const zodDecimalKind = "ZodDecimal";
export interface ZodDecimalDef extends ZodTypeDef {
checks: ZodDecimalCheck[];
typeName: typeof zodDecimalKind;
coerce: boolean;
}
const precisionRegex = /(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export class ZodDecimal extends ZodType<number, ZodDecimalDef, any> {
// eslint-disable-next-line @typescript-eslint/naming-convention
_parse(input: ParseInput): ParseReturnType<number> {
// detect decimal js object
if (
input.data !== null &&
typeof input.data === "object" &&
"toNumber" in input.data
) {
input.data = input.data.toNumber();
}
if (this._def.coerce) {
input.data = Number(input.data);
}
const parsedType = this._getType(input);
if (parsedType !== ZodParsedType.number) {
const ctx = this._getOrReturnCtx(input);
addIssueToContext(ctx, {
code: ZodIssueCode.invalid_type,
expected: ZodParsedType.number,
received: ctx.parsedType,
});
return INVALID;
}
let ctx: undefined | ParseContext = undefined;
const status = new ParseStatus();
for (const check of this._def.checks) {
if (check.kind === "precision") {
const parts = input.data.toString().match(precisionRegex);
const decimals = Math.max(
(parts[1] ? parts[1].length : 0) -
(parts[2] ? parseInt(parts[2], 10) : 0),
0
);
if (decimals > check.value) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
code: ZodIssueCode.custom,
message: check.message,
params: {
precision: check.value,
},
});
status.dirty();
}
} else if (check.kind === "wholeNumber") {
const wholeNumber = input.data.toString().split(".")[0];
const tooLong = wholeNumber.length > check.value;
if (tooLong) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
code: ZodIssueCode.custom,
message: check.message,
params: {
wholeNumber: check.value,
},
});
status.dirty();
}
} else if (check.kind === "min") {
const tooSmall = check.inclusive
? input.data < check.value
: input.data <= check.value;
if (tooSmall) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
code: ZodIssueCode.too_small,
minimum: check.value,
type: "number",
inclusive: check.inclusive,
exact: false,
message: check.message,
});
status.dirty();
}
} else if (check.kind === "max") {
const tooBig = check.inclusive
? input.data > check.value
: input.data >= check.value;
if (tooBig) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
code: ZodIssueCode.too_big,
maximum: check.value,
type: "number",
inclusive: check.inclusive,
exact: false,
message: check.message,
});
status.dirty();
}
} else if (check.kind === "finite") {
if (!Number.isFinite(input.data)) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
code: ZodIssueCode.not_finite,
message: check.message,
});
status.dirty();
}
}
}
return { status: status.value, value: input.data };
}
static create = (
params?: RawCreateParams & { coerce?: true }
): ZodDecimal => {
return new ZodDecimal({
checks: [],
typeName: zodDecimalKind,
coerce: params?.coerce ?? false,
});
};
protected setLimit(
kind: "min" | "max",
value: number,
inclusive: boolean,
message?: string
): ZodDecimal {
return new ZodDecimal({
...this._def,
checks: [
...this._def.checks,
{
kind,
value,
inclusive,
message,
},
],
});
}
_addCheck(check: ZodDecimalCheck): ZodDecimal {
return new ZodDecimal({
...this._def,
checks: [...this._def.checks, check],
});
}
lte(value: number, message?: string): ZodDecimal {
return this.setLimit("max", value, true, message);
}
lt(value: number, message?: string): ZodDecimal {
return this.setLimit("max", value, false, message);
}
max = this.lte;
gt(value: number, message?: string): ZodDecimal {
return this.setLimit("min", value, false, message);
}
gte(value: number, message?: string): ZodDecimal {
return this.setLimit("min", value, true, message);
}
min = this.gte;
precision(value: number, message?: string): ZodDecimal {
return this._addCheck({
kind: "precision",
value,
message,
});
}
wholeNumber(value: number, message?: string): ZodDecimal {
return this._addCheck({
kind: "wholeNumber",
value,
message,
});
}
get minValue() {
let min: number | null = null;
for (const ch of this._def.checks) {
if (ch.kind === "min") {
if (min === null || ch.value > min) min = ch.value;
}
}
return min;
}
get maxValue() {
let max: number | null = null;
for (const ch of this._def.checks) {
if (ch.kind === "max") {
if (max === null || ch.value < max) max = ch.value;
}
}
return max;
}
positive(message?: string) {
return this._addCheck({
kind: "min",
value: 0,
inclusive: false,
message,
});
}
negative(message?: string) {
return this._addCheck({
kind: "max",
value: 0,
inclusive: false,
message,
});
}
nonpositive(message?: string) {
return this._addCheck({
kind: "max",
value: 0,
inclusive: true,
message,
});
}
nonnegative(message?: string) {
return this._addCheck({
kind: "min",
value: 0,
inclusive: true,
message,
});
}
finite(message?: string) {
return this._addCheck({
kind: "finite",
message,
});
}
safe(message?: string) {
return this._addCheck({
kind: "min",
inclusive: true,
value: Number.MIN_SAFE_INTEGER,
message,
})._addCheck({
kind: "max",
inclusive: true,
value: Number.MAX_SAFE_INTEGER,
message,
});
}
get isFinite() {
let max: number | null = null,
min: number | null = null;
for (const ch of this._def.checks) {
if (ch.kind === "finite") {
return true;
} else if (ch.kind === "min") {
if (min === null || ch.value > min) min = ch.value;
} else if (ch.kind === "max") {
if (max === null || ch.value < max) max = ch.value;
}
}
return Number.isFinite(min) && Number.isFinite(max);
}
}
// eslint-disable-next-line @typescript-eslint/naming-convention
export const zodDecimal = ZodDecimal.create;