+
+
Predict the Future with ARIMA
@@ -22,7 +22,7 @@
-
+
\ No newline at end of file
diff --git a/components/my/form/input-with-type.vue b/components/my/form/input-with-type.vue
new file mode 100644
index 0000000..dc647dc
--- /dev/null
+++ b/components/my/form/input-with-type.vue
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/components/my/input-currency.vue b/components/my/input-currency.vue
new file mode 100644
index 0000000..8b3c051
--- /dev/null
+++ b/components/my/input-currency.vue
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/components/my/input-number.vue b/components/my/input-number.vue
new file mode 100644
index 0000000..f15a7fe
--- /dev/null
+++ b/components/my/input-number.vue
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/components/my/modal/update-prediction-row.vue b/components/my/modal/update-prediction-row.vue
new file mode 100644
index 0000000..1f35e87
--- /dev/null
+++ b/components/my/modal/update-prediction-row.vue
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/composables/usePredictionTable.ts b/composables/usePredictionTable.ts
new file mode 100644
index 0000000..8b24a7a
--- /dev/null
+++ b/composables/usePredictionTable.ts
@@ -0,0 +1,38 @@
+import type { TableColumn } from '#ui/types'
+export function usePredictionTableColumn() {
+ const requiredColumn = ['date', 'product code', 'product name', 'sold(qty)']
+ const columnKeys = ref
([])
+ const columns: ComputedRef = computed(() => {
+ if (columnKeys.value.length >= 1) {
+ return [{
+ key: 'actions',
+ sortable: true,
+ label: 'Actions'
+ }, ...columnKeys.value.map(v => ({
+ key: v,
+ sortable: true,
+ label: v
+ }) as TableColumn)]
+ } else {
+ return [{
+ key: 'actions',
+ sortable: true,
+ label: 'Actions'
+ }, ...requiredColumn.map(v => ({
+ key: v,
+ sortable: true,
+ label: v,
+ }))]
+ }
+ })
+ const missingColumn = computed(() => {
+ const currentColumn = columns.value.map(v => v.key)
+ return requiredColumn.filter(v => !currentColumn.includes(v))
+ })
+
+ return {
+ columnKeys,
+ columns,
+ missingColumn
+ }
+}
\ No newline at end of file
diff --git a/composables/useSpreadsheet.ts b/composables/useSpreadsheet.ts
new file mode 100644
index 0000000..5a43b49
--- /dev/null
+++ b/composables/useSpreadsheet.ts
@@ -0,0 +1,45 @@
+import { sheetToJSON } from "~/utils/spreadsheet/sheetsToJSON"
+
+export function useFileToJSON() {
+ const toast = useToast()
+ const file = ref(null)
+ const status = ref<'idle' | 'loading' | 'error' | 'success'>('idle')
+ const result = ref[]>([])
+ const error = ref(null)
+ watch(file, async (newVal) => {
+ if (!newVal)
+ return
+ status.value = 'loading'
+ error.value = null
+ try {
+ const json = await sheetToJSON(newVal);
+ if (json) {
+ result.value = json as Record[]
+ }
+ } catch (e: unknown) {
+ status.value = 'error'
+ if (e instanceof Error) {
+ error.value = e
+ }
+ toast.add({
+ title: 'Error',
+ icon: 'i-heroicons-x-circle',
+ color: 'red',
+ description: error.value?.message
+ })
+ } finally {
+ if (status.value !== 'error') {
+ status.value = 'success'
+ toast.add({
+ title: 'Success',
+ icon: 'i-heroicons-document-check',
+ color: 'green',
+ description: 'File Imported Successfully.'
+ })
+ }
+ }
+ })
+ return {
+ file, status, result, error
+ }
+}
\ No newline at end of file
diff --git a/nuxt.config.ts b/nuxt.config.ts
index a2a0147..70ceaaa 100644
--- a/nuxt.config.ts
+++ b/nuxt.config.ts
@@ -12,7 +12,7 @@ export default defineNuxtConfig({
},
compatibilityDate: '2024-11-01',
devtools: { enabled: true },
- modules: ['@nuxt/image', '@nuxt/ui', 'shadcn-nuxt'],
+ modules: ['@nuxt/image', '@nuxt/ui', 'shadcn-nuxt', 'dayjs-nuxt'],
ui: {
prefix: 'NuxtUi'
},
@@ -30,5 +30,8 @@ export default defineNuxtConfig({
image: {
format: ['webp'],
quality: 80,
- }
-})
\ No newline at end of file
+ },
+ css: [
+ '@/assets/css/main.tw.css'
+ ]
+})
diff --git a/package-lock.json b/package-lock.json
index fc28c3f..028b9b0 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -12,14 +12,18 @@
"@vueuse/core": "^13.0.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
+ "date-fns": "^2.30.0",
+ "dayjs-nuxt": "^2.1.11",
"lucide-vue-next": "^0.485.0",
"nuxt": "^3.16.1",
"reka-ui": "^2.2.0",
"shadcn-nuxt": "^1.0.3",
"tailwind-merge": "^3.0.2",
"tailwindcss-animate": "^1.0.7",
+ "v-calendar": "^3.1.2",
"vue": "^3.5.13",
"vue-router": "^4.5.0",
+ "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz",
"zod": "^3.24.2"
}
},
@@ -422,6 +426,18 @@
"@babel/core": "^7.0.0-0"
}
},
+ "node_modules/@babel/runtime": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz",
+ "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==",
+ "license": "MIT",
+ "dependencies": {
+ "regenerator-runtime": "^0.14.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
"node_modules/@babel/template": {
"version": "7.27.0",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz",
@@ -2953,12 +2969,24 @@
"integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==",
"license": "MIT"
},
+ "node_modules/@types/lodash": {
+ "version": "4.17.16",
+ "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.16.tgz",
+ "integrity": "sha512-HX7Em5NYQAXKW+1T+FiuG27NGwzJfCX3s1GjOa7ujxZa52kjJLOr4FUxT+giF6Tgxv1e+/czV/iTtBw27WTU9g==",
+ "license": "MIT"
+ },
"node_modules/@types/parse-path": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/@types/parse-path/-/parse-path-7.0.3.tgz",
"integrity": "sha512-LriObC2+KYZD3FzCrgWGv/qufdUy4eXrxcLgQMfYXgPbLIecKIsVBaQgUPmxSSLcjmYbDTQbMgr6qr6l/eb7Bg==",
"license": "MIT"
},
+ "node_modules/@types/resize-observer-browser": {
+ "version": "0.1.11",
+ "resolved": "https://registry.npmjs.org/@types/resize-observer-browser/-/resize-observer-browser-0.1.11.tgz",
+ "integrity": "sha512-cNw5iH8JkMkb3QkCoe7DaZiawbDQEUX8t7iuQaRTyLOyQCR2h+ibBD4GJt7p5yhUHrlOeL7ZtbxNHeipqNsBzQ==",
+ "license": "MIT"
+ },
"node_modules/@types/resolve": {
"version": "1.20.2",
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz",
@@ -4826,6 +4854,47 @@
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
"license": "MIT"
},
+ "node_modules/date-fns": {
+ "version": "2.30.0",
+ "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz",
+ "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.21.0"
+ },
+ "engines": {
+ "node": ">=0.11"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/date-fns"
+ }
+ },
+ "node_modules/date-fns-tz": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/date-fns-tz/-/date-fns-tz-2.0.1.tgz",
+ "integrity": "sha512-fJCG3Pwx8HUoLhkepdsP7Z5RsucUi+ZBOxyM5d0ZZ6c4SdYustq0VMmOu6Wf7bli+yS/Jwp91TOCqn9jMcVrUA==",
+ "license": "MIT",
+ "peerDependencies": {
+ "date-fns": "2.x"
+ }
+ },
+ "node_modules/dayjs": {
+ "version": "1.11.13",
+ "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz",
+ "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==",
+ "license": "MIT"
+ },
+ "node_modules/dayjs-nuxt": {
+ "version": "2.1.11",
+ "resolved": "https://registry.npmjs.org/dayjs-nuxt/-/dayjs-nuxt-2.1.11.tgz",
+ "integrity": "sha512-KDDNiET7KAKf6yzL3RaPWq5aV7ql9QTt5fIDYv+4eOegDmnEQGjwkKYADDystsKtPjt7QZerpVbhC96o3BIyqQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@nuxt/kit": "^3.7.4",
+ "dayjs": "^1.11.10"
+ }
+ },
"node_modules/db0": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/db0/-/db0-0.3.1.tgz",
@@ -8904,6 +8973,12 @@
"node": ">=4"
}
},
+ "node_modules/regenerator-runtime": {
+ "version": "0.14.1",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
+ "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
+ "license": "MIT"
+ },
"node_modules/reka-ui": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/reka-ui/-/reka-ui-2.2.0.tgz",
@@ -10852,6 +10927,24 @@
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"license": "MIT"
},
+ "node_modules/v-calendar": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/v-calendar/-/v-calendar-3.1.2.tgz",
+ "integrity": "sha512-QDWrnp4PWCpzUblctgo4T558PrHgHzDtQnTeUNzKxfNf29FkCeFpwGd9bKjAqktaa2aJLcyRl45T5ln1ku34kg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/lodash": "^4.14.165",
+ "@types/resize-observer-browser": "^0.1.7",
+ "date-fns": "^2.16.1",
+ "date-fns-tz": "^2.0.0",
+ "lodash": "^4.17.20",
+ "vue-screen-utils": "^1.0.0-beta.13"
+ },
+ "peerDependencies": {
+ "@popperjs/core": "^2.0.0",
+ "vue": "^3.2.0"
+ }
+ },
"node_modules/vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
@@ -11221,6 +11314,15 @@
"vue": "^3.2.0"
}
},
+ "node_modules/vue-screen-utils": {
+ "version": "1.0.0-beta.13",
+ "resolved": "https://registry.npmjs.org/vue-screen-utils/-/vue-screen-utils-1.0.0-beta.13.tgz",
+ "integrity": "sha512-EJ/8TANKhFj+LefDuOvZykwMr3rrLFPLNb++lNBqPOpVigT2ActRg6icH9RFQVm4nHwlHIHSGm5OY/Clar9yIg==",
+ "license": "MIT",
+ "peerDependencies": {
+ "vue": "^3.2.0"
+ }
+ },
"node_modules/webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
@@ -11376,6 +11478,18 @@
}
}
},
+ "node_modules/xlsx": {
+ "version": "0.20.3",
+ "resolved": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz",
+ "integrity": "sha512-oLDq3jw7AcLqKWH2AhCpVTZl8mf6X2YReP+Neh0SJUzV/BdZYjth94tG5toiMB1PPrYtxOCfaoUCkvtuH+3AJA==",
+ "license": "Apache-2.0",
+ "bin": {
+ "xlsx": "bin/xlsx.njs"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
"node_modules/xss": {
"version": "1.0.15",
"resolved": "https://registry.npmjs.org/xss/-/xss-1.0.15.tgz",
diff --git a/package.json b/package.json
index af5f9eb..a7e0b0c 100644
--- a/package.json
+++ b/package.json
@@ -15,14 +15,18 @@
"@vueuse/core": "^13.0.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
+ "date-fns": "^2.30.0",
+ "dayjs-nuxt": "^2.1.11",
"lucide-vue-next": "^0.485.0",
"nuxt": "^3.16.1",
"reka-ui": "^2.2.0",
"shadcn-nuxt": "^1.0.3",
"tailwind-merge": "^3.0.2",
"tailwindcss-animate": "^1.0.7",
+ "v-calendar": "^3.1.2",
"vue": "^3.5.13",
"vue-router": "^4.5.0",
+ "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz",
"zod": "^3.24.2"
}
}
diff --git a/pages/demo.vue b/pages/demo.vue
index 4a91088..4bbf4e4 100644
--- a/pages/demo.vue
+++ b/pages/demo.vue
@@ -1,7 +1,91 @@
-
- demo page
+
{
+ if (convertStatus !== 'loading') {
+ handleDragFile(e)
+ }
+ }">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ rowsData }}
+
+
diff --git a/types/landing-page/demo/modalMakePrediction.ts b/types/landing-page/demo/modalMakePrediction.ts
new file mode 100644
index 0000000..6710c44
--- /dev/null
+++ b/types/landing-page/demo/modalMakePrediction.ts
@@ -0,0 +1,13 @@
+export type TDurationType = 'daily' | 'weekly' | 'monthly'
+export type TModalMakePredictionModel = {
+ recordPeriod?: TDurationType,
+ selectedProduct?: string,
+ predictionPeriod?: TDurationType,
+}
+export type TProduct = {
+ product_code: string,
+ product_name: string
+}
+export type TModalMakePredictionProps = {
+ products?: TProduct[]
+}
\ No newline at end of file
diff --git a/utils/spreadsheet/sheetsToJSON.ts b/utils/spreadsheet/sheetsToJSON.ts
new file mode 100644
index 0000000..fa658f2
--- /dev/null
+++ b/utils/spreadsheet/sheetsToJSON.ts
@@ -0,0 +1,13 @@
+import * as XLSX from 'xlsx';
+
+export async function sheetToJSON(file: File) {
+ try {
+ const fileBuffer = await file.arrayBuffer();
+ const workbook = XLSX.read(fileBuffer);
+ const sheet = workbook.Sheets[workbook.SheetNames[0]]
+ const json = XLSX.utils.sheet_to_json(sheet)
+ return json
+ } catch (error: unknown) {
+ throw error
+ }
+}
\ No newline at end of file