diff --git a/.gitignore b/.gitignore
index 82965e2d..d86875dd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -40,8 +40,5 @@ yarn-error.log*
*.tsbuildinfo
next-env.d.ts
-# prettier
-.prettierrc
-
# idea
.idea
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index efda72f0..951e5472 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,76 +1,146 @@
-stages: [notify]
+stages:
+ - build
+ - deploy
-# --- Notify when MR is opened/updated ---
-notify_discord_mr:
- stage: notify
- image: alpine:3.20
- rules:
- - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
+.build_template: &build_template
+ stage: build
+ image: node:20-alpine
+ cache:
+ key: npm-cache
+ paths:
+ - node_modules/
variables:
- WEBHOOK_URL: $DISCORD_WEBHOOK_URL
- before_script:
- - apk add --no-cache curl jq
- script: |
- MR_URL="${CI_PROJECT_URL}/-/merge_requests/${CI_MERGE_REQUEST_IID}"
+ NPM_CONFIG_PRODUCTION: 'false'
+ NODE_ENV: ''
+ script:
+ - echo "Installing dependencies..."
+ - npm ci --no-audit --no-fund
+ - echo "Building Next.js static export..."
+ - npx next build
+ artifacts:
+ name: 'out-$CI_COMMIT_SHORT_SHA'
+ paths:
+ - out/
+ expire_in: 1 week
- jq -n \
- --arg repo "$CI_PROJECT_PATH" \
- --arg mr "#${CI_MERGE_REQUEST_IID}" \
- --arg url "$MR_URL" \
- --arg requestor "${GITLAB_USER_LOGIN:-$GITLAB_USER_NAME}" \
- --arg source "$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME" \
- --arg target "$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" \
- --arg title "$CI_MERGE_REQUEST_TITLE" \
- '{
- username: "CI Bot - FE",
- embeds: [{
- title: "📣 [LTI WEB CLIENT] Merge Request Opened/Updated",
- description: ($mr + " in " + $repo),
- url: $url,
- color: 3447003,
- fields: [
- {name: "Author", value: $requestor, inline: true},
- {name: "Source → Target", value: ($source + " → " + $target), inline: true},
- {name: "Title", value: $title}
- ]
- }]
- }' \
- | curl -sS -H "Content-Type: application/json" -d @- "$WEBHOOK_URL"
+.deploy_template: &deploy_template
+ stage: deploy
+ image:
+ name: amazon/aws-cli:latest
+ entrypoint: ['/bin/sh', '-c']
+ script:
+ - set -e
+ - aws --version
+ - echo "Cleaning up newline characters in AWS credentials..."
+ - export AWS_ACCESS_KEY_ID=$(echo $AWS_ACCESS_KEY_ID | tr -d '\r\n')
+ - export AWS_SECRET_ACCESS_KEY=$(echo $AWS_SECRET_ACCESS_KEY | tr -d '\r\n')
+ - echo "Deploying to s3://$S3_BUCKET in region $AWS_REGION"
+ - aws s3api head-bucket --bucket "$S3_BUCKET" --region "$AWS_REGION" || aws s3api create-bucket --bucket "$S3_BUCKET" --region "$AWS_REGION" --create-bucket-configuration LocationConstraint="$AWS_REGION"
+ - aws s3 sync ./out "s3://$S3_BUCKET" --delete --region "$AWS_REGION" --endpoint-url "https://s3.ap-southeast-3.amazonaws.com"
-# --- Notify when MR is merged ---
-notify_discord_merge:
- stage: notify
- image: alpine:3.20
+ # CloudFront invalidation
+ - |
+ STATUS="success"
+ if [ -n "$CLOUDFRONT_DISTRIBUTION_ID" ]; then
+ echo "Invalidating CloudFront cache..."
+ if ! aws cloudfront create-invalidation --distribution-id "$CLOUDFRONT_DISTRIBUTION_ID" --paths "/*"; then
+ echo "CloudFront invalidation failed."
+ STATUS="failed"
+ fi
+ else
+ echo "No CloudFront distribution specified — skipping invalidation"
+ fi
+
+ # Notifikasi Discord
+ - |
+ RUN_URL="${CI_PROJECT_URL}/-/pipelines/${CI_PIPELINE_ID}"
+
+ if [ "$CI_COMMIT_BRANCH" = "development" ]; then
+ ENVIRONMENT_NAME="WEB-LTI-DEV"
+ elif [ "$CI_COMMIT_BRANCH" = "master" ]; then
+ ENVIRONMENT_NAME="WEB-LTI-PROD"
+ else
+ ENVIRONMENT_NAME="UNKNOWN"
+ fi
+
+ if [ "$STATUS" = "success" ]; then
+ COLOR=3066993
+ TITLE="✅ Deployment ${ENVIRONMENT_NAME} Succeeded"
+ DESC="Deployment job on branch \`${CI_COMMIT_REF_NAME}\` completed successfully."
+ else
+ COLOR=15158332
+ TITLE="❌ Deployment ${ENVIRONMENT_NAME} Failed"
+ DESC="Deployment job on branch \`${CI_COMMIT_REF_NAME}\` encountered issues."
+ fi
+
+ jq -n \
+ --arg title "$TITLE" \
+ --arg desc "$DESC" \
+ --arg color "$COLOR" \
+ --arg repo "$CI_PROJECT_PATH" \
+ --arg actor "$GITLAB_USER_LOGIN" \
+ --arg commit "$CI_COMMIT_SHA" \
+ --arg run_url "$RUN_URL" \
+ '{
+ username: "CI Bot - LTI WEB",
+ embeds: [{
+ title: $title,
+ description: $desc,
+ color: ($color|tonumber),
+ fields: [
+ {name: "Repository", value: $repo, inline: true},
+ {name: "Actor", value: $actor, inline: true},
+ {name: "Commit", value: $commit, inline: false},
+ {name: "Pipeline", value: ("[Open run](" + $run_url + ")"), inline: false}
+ ]
+ }]
+ }' > payload.json
+
+ curl -sS -H "Content-Type: application/json" -d @payload.json "$DISCORD_WEBHOOK_URL"
+
+# ====== DEVELOPMENT (Branch development) ======
+build:dev:
+ <<: *build_template
rules:
- # Only run for merge request pipelines that are in merged state
- - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_STATE == "merged"'
+ - if: '$CI_COMMIT_BRANCH == "development"'
+ environment:
+ name: development
variables:
- WEBHOOK_URL: $DISCORD_WEBHOOK_URL
- before_script:
- - apk add --no-cache curl jq
- script: |
- MR_URL="${CI_PROJECT_URL}/-/merge_requests/${CI_MERGE_REQUEST_IID}"
+ NEXT_PUBLIC_API_BASE_URL: 'https://dev-api-lti.mbugroup.id'
+ NEXT_PUBLIC_SSO_LOGIN_URL: 'https://dev-api-sso.mbugroup.id'
+
+deploy:dev:
+ <<: *deploy_template
+ needs: ['build:dev']
+ rules:
+ - if: '$CI_COMMIT_BRANCH == "development"'
+ variables:
+ S3_BUCKET: 'dev-lti-erp.mbugroup.id'
+ AWS_REGION: 'ap-southeast-3'
+ CLOUDFRONT_DISTRIBUTION_ID: 'E1Z8XTA8XF1GIV'
+ environment:
+ name: development
+ url: https://dev-lti-erp.mbugroup.id
+# ====== PRODUCTION ======
+# build:production:
+# <<: *build_template
+# rules:
+# # pilih salah satu: pakai branch master ATAU pakai tags rilis
+# - if: '$CI_COMMIT_BRANCH == "master"'
+# # - if: '$CI_COMMIT_TAG' # kalau mau rilis via tag, uncomment ini dan hapus baris di atas
+# environment:
+# name: production
+
+# deploy:production:
+# <<: *deploy_template
+# needs: ["build:production"]
+# rules:
+# - if: '$CI_COMMIT_BRANCH == "master"'
+# # - if: '$CI_COMMIT_TAG' # selaras dengan rule di build:production
+# variables:
+# S3_BUCKET: "lti-erp.mbugroup.id"
+# CLOUDFRONT_DISTRIBUTION_ID: "ddfd"
+# environment:
+# name: production
+# url: https://royalgoldcapital.com
- jq -n \
- --arg repo "$CI_PROJECT_PATH" \
- --arg mr "#${CI_MERGE_REQUEST_IID}" \
- --arg url "$MR_URL" \
- --arg requestor "${CI_MERGE_REQUEST_AUTHOR}" \
- --arg source "$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME" \
- --arg target "$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" \
- --arg title "$CI_MERGE_REQUEST_TITLE" \
- '{
- username: "CI Bot - FE",
- embeds: [{
- title: "✅ [LTI WEB CLIENT] Merge Request Merged",
- description: ($mr + " has been merged into " + $repo),
- url: $url,
- color: 3066993,
- fields: [
- {name: "Author", value: $requestor, inline: true},
- {name: "Source → Target", value: ($source + " → " + $target), inline: true},
- {name: "Title", value: $title}
- ]
- }]
- }' \
- | curl -sS -H "Content-Type: application/json" -d @- "$WEBHOOK_URL"
diff --git a/.husky/pre-commit b/.husky/pre-commit
index 66ff6a67..e7bb3165 100644
--- a/.husky/pre-commit
+++ b/.husky/pre-commit
@@ -1,2 +1,3 @@
+npm run format
npm run lint
npm run build
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 00000000..a3a2e197
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,25 @@
+FROM node:20-alpine
+
+RUN apk add --no-cache git bash build-base curl
+
+WORKDIR /app
+
+COPY package*.json ./
+RUN npm ci
+
+COPY . .
+
+# Buat config agar Next tahu output: export
+RUN echo "const config = { output: 'export', images: { unoptimized: true } }; export default config;" > next.config.mjs
+
+# Build project (Next.js 15 otomatis static export)
+RUN NEXT_DISABLE_TURBOPACK=1 npx next build
+
+# Copy static assets dan hasil build agar bisa diakses
+RUN mkdir -p .next/server/app/_next && \
+ cp -r .next/static .next/server/app/_next/static && \
+ cp -r public/* .next/server/app/
+
+EXPOSE 3000
+
+CMD ["npx", "serve", ".next/server/app", "-l", "3000"]
\ No newline at end of file
diff --git a/docker-compose.yaml b/docker-compose.yaml
new file mode 100644
index 00000000..b89f441b
--- /dev/null
+++ b/docker-compose.yaml
@@ -0,0 +1,39 @@
+version: '3.9'
+
+services:
+ dev-web-lti:
+ container_name: dev-web-lti
+ build:
+ context: .
+ dockerfile: Dockerfile
+ ports:
+ - '3002:3000'
+ env_file:
+ - .env
+ environment:
+ NODE_ENV: production
+ APP_ENV: production
+ networks:
+ - dev-lti-network
+ restart: always
+ deploy:
+ resources:
+ limits:
+ cpus: '3.0'
+ memory: 3G
+ reservations:
+ cpus: '1.0'
+ memory: 512M
+ extra_hosts:
+ - 'host.docker.internal:host-gateway'
+ # Optional: aktifkan healthcheck jika punya endpoint
+ # healthcheck:
+ # test: ["CMD-SHELL", "curl -fsS http://localhost:3000/api/healthz || exit 1"]
+ # interval: 10s
+ # timeout: 3s
+ # retries: 10
+ # start_period: 15s
+
+networks:
+ dev-lti-network:
+ external: true
diff --git a/eslint.config.mjs b/eslint.config.mjs
index 719cea2b..fa167c8d 100644
--- a/eslint.config.mjs
+++ b/eslint.config.mjs
@@ -1,6 +1,6 @@
-import { dirname } from "path";
-import { fileURLToPath } from "url";
-import { FlatCompat } from "@eslint/eslintrc";
+import { dirname } from 'path';
+import { fileURLToPath } from 'url';
+import { FlatCompat } from '@eslint/eslintrc';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
@@ -10,14 +10,14 @@ const compat = new FlatCompat({
});
const eslintConfig = [
- ...compat.extends("next/core-web-vitals", "next/typescript"),
+ ...compat.extends('next/core-web-vitals', 'next/typescript'),
{
ignores: [
- "node_modules/**",
- ".next/**",
- "out/**",
- "build/**",
- "next-env.d.ts",
+ 'node_modules/**',
+ '.next/**',
+ 'out/**',
+ 'build/**',
+ 'next-env.d.ts',
],
},
];
diff --git a/package-lock.json b/package-lock.json
index e1f28d3e..ec1316ae 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,16 +8,18 @@
"name": "lti-web-client",
"version": "0.1.0",
"dependencies": {
+ "@react-pdf/renderer": "^4.3.1",
"@tanstack/match-sorter-utils": "^8.19.4",
"@tanstack/react-table": "^8.21.3",
"axios": "^1.12.2",
"clsx": "^2.1.1",
"formik": "^2.4.6",
- "inputmask": "^5.0.9",
"moment": "^2.30.1",
"next": "15.5.3",
"react": "19.1.0",
+ "react-day-picker": "^9.11.1",
"react-dom": "19.1.0",
+ "react-dropzone": "^14.3.8",
"react-hot-toast": "^2.6.0",
"react-number-format": "^5.4.4",
"react-select": "^5.10.2",
@@ -31,7 +33,6 @@
"@eslint/eslintrc": "^3",
"@iconify/react": "^6.0.2",
"@tailwindcss/postcss": "^4",
- "@types/inputmask": "^5.0.7",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
@@ -39,6 +40,7 @@
"eslint": "^9",
"eslint-config-next": "15.5.3",
"husky": "^9.1.7",
+ "prettier": "^3.6.2",
"tailwindcss": "^4",
"typescript": "^5"
}
@@ -195,6 +197,12 @@
"node": ">=6.9.0"
}
},
+ "node_modules/@date-fns/tz": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/@date-fns/tz/-/tz-1.4.1.tgz",
+ "integrity": "sha512-P5LUNhtbj6YfI3iJjw5EL9eUAG6OitD0W3fWQcpQjDRc/QIsL0tRNuO1PcDvPccWL1fSTXXdE1ds+l95DV/OFA==",
+ "license": "MIT"
+ },
"node_modules/@emnapi/core": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.6.0.tgz",
@@ -1265,6 +1273,180 @@
"node": ">=12.4.0"
}
},
+ "node_modules/@react-pdf/fns": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@react-pdf/fns/-/fns-3.1.2.tgz",
+ "integrity": "sha512-qTKGUf0iAMGg2+OsUcp9ffKnKi41RukM/zYIWMDJ4hRVYSr89Q7e3wSDW/Koqx3ea3Uy/z3h2y3wPX6Bdfxk6g==",
+ "license": "MIT"
+ },
+ "node_modules/@react-pdf/font": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/@react-pdf/font/-/font-4.0.3.tgz",
+ "integrity": "sha512-N1qQDZr6phXYQOp033Hvm2nkUkx2LkszjGPbmRavs9VOYzi4sp31MaccMKptL24ii6UhBh/z9yPUhnuNe/qHwA==",
+ "license": "MIT",
+ "dependencies": {
+ "@react-pdf/pdfkit": "^4.0.4",
+ "@react-pdf/types": "^2.9.1",
+ "fontkit": "^2.0.2",
+ "is-url": "^1.2.4"
+ }
+ },
+ "node_modules/@react-pdf/image": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@react-pdf/image/-/image-3.0.3.tgz",
+ "integrity": "sha512-lvP5ryzYM3wpbO9bvqLZYwEr5XBDX9jcaRICvtnoRqdJOo7PRrMnmB4MMScyb+Xw10mGeIubZAAomNAG5ONQZQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@react-pdf/png-js": "^3.0.0",
+ "jay-peg": "^1.1.1"
+ }
+ },
+ "node_modules/@react-pdf/layout": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/@react-pdf/layout/-/layout-4.4.1.tgz",
+ "integrity": "sha512-GVzdlWoZWldRDzlWj3SttRXmVDxg7YfraAohwy+o9gb9hrbDJaaAV6jV3pc630Evd3K46OAzk8EFu8EgPDuVuA==",
+ "license": "MIT",
+ "dependencies": {
+ "@react-pdf/fns": "3.1.2",
+ "@react-pdf/image": "^3.0.3",
+ "@react-pdf/primitives": "^4.1.1",
+ "@react-pdf/stylesheet": "^6.1.1",
+ "@react-pdf/textkit": "^6.0.0",
+ "@react-pdf/types": "^2.9.1",
+ "emoji-regex-xs": "^1.0.0",
+ "queue": "^6.0.1",
+ "yoga-layout": "^3.2.1"
+ }
+ },
+ "node_modules/@react-pdf/pdfkit": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/@react-pdf/pdfkit/-/pdfkit-4.0.4.tgz",
+ "integrity": "sha512-/nITLggsPlB66bVLnm0X7MNdKQxXelLGZG6zB5acF5cCgkFwmXHnLNyxYOUD4GMOMg1HOPShXDKWrwk2ZeHsvw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.20.13",
+ "@react-pdf/png-js": "^3.0.0",
+ "browserify-zlib": "^0.2.0",
+ "crypto-js": "^4.2.0",
+ "fontkit": "^2.0.2",
+ "jay-peg": "^1.1.1",
+ "linebreak": "^1.1.0",
+ "vite-compatible-readable-stream": "^3.6.1"
+ }
+ },
+ "node_modules/@react-pdf/png-js": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@react-pdf/png-js/-/png-js-3.0.0.tgz",
+ "integrity": "sha512-eSJnEItZ37WPt6Qv5pncQDxLJRK15eaRwPT+gZoujP548CodenOVp49GST8XJvKMFt9YqIBzGBV/j9AgrOQzVA==",
+ "license": "MIT",
+ "dependencies": {
+ "browserify-zlib": "^0.2.0"
+ }
+ },
+ "node_modules/@react-pdf/primitives": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/@react-pdf/primitives/-/primitives-4.1.1.tgz",
+ "integrity": "sha512-IuhxYls1luJb7NUWy6q5avb1XrNaVj9bTNI40U9qGRuS6n7Hje/8H8Qi99Z9UKFV74bBP3DOf3L1wV2qZVgVrQ==",
+ "license": "MIT"
+ },
+ "node_modules/@react-pdf/reconciler": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@react-pdf/reconciler/-/reconciler-1.1.4.tgz",
+ "integrity": "sha512-oTQDiR/t4Z/Guxac88IavpU2UgN7eR0RMI9DRKvKnvPz2DUasGjXfChAdMqDNmJJxxV26mMy9xQOUV2UU5/okg==",
+ "license": "MIT",
+ "dependencies": {
+ "object-assign": "^4.1.1",
+ "scheduler": "0.25.0-rc-603e6108-20241029"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/@react-pdf/reconciler/node_modules/scheduler": {
+ "version": "0.25.0-rc-603e6108-20241029",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0-rc-603e6108-20241029.tgz",
+ "integrity": "sha512-pFwF6H1XrSdYYNLfOcGlM28/j8CGLu8IvdrxqhjWULe2bPcKiKW4CV+OWqR/9fT52mywx65l7ysNkjLKBda7eA==",
+ "license": "MIT"
+ },
+ "node_modules/@react-pdf/render": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@react-pdf/render/-/render-4.3.1.tgz",
+ "integrity": "sha512-v1WAaAhQShQZGcBxfjkEThGCHVH9CSuitrZ1bIOLvB5iBKM14abYK5D6djKhWCwF6FTzYeT2WRjRMVgze/ND2A==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.20.13",
+ "@react-pdf/fns": "3.1.2",
+ "@react-pdf/primitives": "^4.1.1",
+ "@react-pdf/textkit": "^6.0.0",
+ "@react-pdf/types": "^2.9.1",
+ "abs-svg-path": "^0.1.1",
+ "color-string": "^1.9.1",
+ "normalize-svg-path": "^1.1.0",
+ "parse-svg-path": "^0.1.2",
+ "svg-arc-to-cubic-bezier": "^3.2.0"
+ }
+ },
+ "node_modules/@react-pdf/renderer": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@react-pdf/renderer/-/renderer-4.3.1.tgz",
+ "integrity": "sha512-dPKHiwGTaOsKqNWCHPYYrx8CDfAGsUnV4tvRsEu0VPGxuot1AOq/M+YgfN/Pb+MeXCTe2/lv6NvA8haUtj3tsA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.20.13",
+ "@react-pdf/fns": "3.1.2",
+ "@react-pdf/font": "^4.0.3",
+ "@react-pdf/layout": "^4.4.1",
+ "@react-pdf/pdfkit": "^4.0.4",
+ "@react-pdf/primitives": "^4.1.1",
+ "@react-pdf/reconciler": "^1.1.4",
+ "@react-pdf/render": "^4.3.1",
+ "@react-pdf/types": "^2.9.1",
+ "events": "^3.3.0",
+ "object-assign": "^4.1.1",
+ "prop-types": "^15.6.2",
+ "queue": "^6.0.1"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/@react-pdf/stylesheet": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/@react-pdf/stylesheet/-/stylesheet-6.1.1.tgz",
+ "integrity": "sha512-Iyw0A3wRIeQLN4EkaKf8yF9MvdMxiZ8JjoyzLzDHSxnKYoOA4UGu84veCb8dT9N8MxY5x7a0BUv/avTe586Plg==",
+ "license": "MIT",
+ "dependencies": {
+ "@react-pdf/fns": "3.1.2",
+ "@react-pdf/types": "^2.9.1",
+ "color-string": "^1.9.1",
+ "hsl-to-hex": "^1.0.0",
+ "media-engine": "^1.0.3",
+ "postcss-value-parser": "^4.1.0"
+ }
+ },
+ "node_modules/@react-pdf/textkit": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/@react-pdf/textkit/-/textkit-6.0.0.tgz",
+ "integrity": "sha512-fDt19KWaJRK/n2AaFoVm31hgGmpygmTV7LsHGJNGZkgzXcFyLsx+XUl63DTDPH3iqxj3xUX128t104GtOz8tTw==",
+ "license": "MIT",
+ "dependencies": {
+ "@react-pdf/fns": "3.1.2",
+ "bidi-js": "^1.0.2",
+ "hyphen": "^1.6.4",
+ "unicode-properties": "^1.4.1"
+ }
+ },
+ "node_modules/@react-pdf/types": {
+ "version": "2.9.1",
+ "resolved": "https://registry.npmjs.org/@react-pdf/types/-/types-2.9.1.tgz",
+ "integrity": "sha512-5GoCgG0G5NMgpPuHbKG2xcVRQt7+E5pg3IyzVIIozKG3nLcnsXW4zy25vG1ZBQA0jmo39q34au/sOnL/0d1A4w==",
+ "license": "MIT",
+ "dependencies": {
+ "@react-pdf/font": "^4.0.3",
+ "@react-pdf/primitives": "^4.1.1",
+ "@react-pdf/stylesheet": "^6.1.1"
+ }
+ },
"node_modules/@rtsao/scc": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
@@ -1638,13 +1820,6 @@
"@types/react": "*"
}
},
- "node_modules/@types/inputmask": {
- "version": "5.0.7",
- "resolved": "https://registry.npmjs.org/@types/inputmask/-/inputmask-5.0.7.tgz",
- "integrity": "sha512-uojbVPWzBQ/n/0jc/d16fLqmGasFIptbrLD2WrCPWArlk+5PgblOqH4EDkI3AoobXLAlOK5yF01V8jMmvMG5qg==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/@types/json-schema": {
"version": "7.0.15",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
@@ -2260,6 +2435,12 @@
"win32"
]
},
+ "node_modules/abs-svg-path": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/abs-svg-path/-/abs-svg-path-0.1.1.tgz",
+ "integrity": "sha512-d8XPSGjfyzlXC3Xx891DJRyZfqk5JU0BJrDQcsWomFIV1/BIzPW5HDH5iDdWpqWaav0YVIEzT1RHTwWr0FFshA==",
+ "license": "MIT"
+ },
"node_modules/acorn": {
"version": "8.15.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
@@ -2516,6 +2697,15 @@
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"license": "MIT"
},
+ "node_modules/attr-accept": {
+ "version": "2.2.5",
+ "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.5.tgz",
+ "integrity": "sha512-0bDNnY/u6pPwHDMoF0FieU354oBi0a8rD9FcsLwzcGWbc8KS8KPIi7y+s13OlVY+gMWc/9xEMUgNE6Qm8ZllYQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/available-typed-arrays": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
@@ -2585,6 +2775,35 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/bidi-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz",
+ "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==",
+ "license": "MIT",
+ "dependencies": {
+ "require-from-string": "^2.0.2"
+ }
+ },
"node_modules/brace-expansion": {
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
@@ -2609,6 +2828,24 @@
"node": ">=8"
}
},
+ "node_modules/brotli": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz",
+ "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==",
+ "license": "MIT",
+ "dependencies": {
+ "base64-js": "^1.1.2"
+ }
+ },
+ "node_modules/browserify-zlib": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz",
+ "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==",
+ "license": "MIT",
+ "dependencies": {
+ "pako": "~1.0.5"
+ }
+ },
"node_modules/call-bind": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
@@ -2710,6 +2947,15 @@
"integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
"license": "MIT"
},
+ "node_modules/clone": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
+ "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
"node_modules/clsx": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
@@ -2736,9 +2982,18 @@
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true,
"license": "MIT"
},
+ "node_modules/color-string": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
+ "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "^1.0.0",
+ "simple-swizzle": "^0.2.2"
+ }
+ },
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
@@ -2795,6 +3050,12 @@
"node": ">= 8"
}
},
+ "node_modules/crypto-js": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
+ "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
+ "license": "MIT"
+ },
"node_modules/csstype": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
@@ -2872,6 +3133,22 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/date-fns": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
+ "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/kossnocorp"
+ }
+ },
+ "node_modules/date-fns-jalali": {
+ "version": "4.1.0-0",
+ "resolved": "https://registry.npmjs.org/date-fns-jalali/-/date-fns-jalali-4.1.0-0.tgz",
+ "integrity": "sha512-hTIP/z+t+qKwBDcmmsnmjWTduxCg+5KfdqWQvb2X/8C9+knYY6epN/pfxdDuyVlSVeFz0sM5eEfwIUQ70U4ckg==",
+ "license": "MIT"
+ },
"node_modules/debug": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
@@ -2969,6 +3246,12 @@
"node": ">=8"
}
},
+ "node_modules/dfa": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/dfa/-/dfa-1.2.0.tgz",
+ "integrity": "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==",
+ "license": "MIT"
+ },
"node_modules/doctrine": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
@@ -3013,6 +3296,12 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/emoji-regex-xs": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex-xs/-/emoji-regex-xs-1.0.0.tgz",
+ "integrity": "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==",
+ "license": "MIT"
+ },
"node_modules/enhanced-resolve": {
"version": "5.18.3",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz",
@@ -3646,11 +3935,19 @@
"node": ">=0.10.0"
}
},
+ "node_modules/events": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
+ "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.x"
+ }
+ },
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
- "dev": true,
"license": "MIT"
},
"node_modules/fast-glob": {
@@ -3720,6 +4017,18 @@
"node": ">=16.0.0"
}
},
+ "node_modules/file-selector": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-2.1.2.tgz",
+ "integrity": "sha512-QgXo+mXTe8ljeqUFaX3QVHc5osSItJ/Km+xpocx0aSqWGMSCf6qYs/VnzZgS864Pjn5iceMRFigeAV7AfTlaig==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.7.0"
+ },
+ "engines": {
+ "node": ">= 12"
+ }
+ },
"node_modules/fill-range": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
@@ -3797,6 +4106,23 @@
}
}
},
+ "node_modules/fontkit": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/fontkit/-/fontkit-2.0.4.tgz",
+ "integrity": "sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g==",
+ "license": "MIT",
+ "dependencies": {
+ "@swc/helpers": "^0.5.12",
+ "brotli": "^1.3.2",
+ "clone": "^2.1.2",
+ "dfa": "^1.2.0",
+ "fast-deep-equal": "^3.1.3",
+ "restructure": "^3.0.0",
+ "tiny-inflate": "^1.0.3",
+ "unicode-properties": "^1.4.0",
+ "unicode-trie": "^2.0.0"
+ }
+ },
"node_modules/for-each": {
"version": "0.3.5",
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
@@ -4150,6 +4476,21 @@
"react-is": "^16.7.0"
}
},
+ "node_modules/hsl-to-hex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/hsl-to-hex/-/hsl-to-hex-1.0.0.tgz",
+ "integrity": "sha512-K6GVpucS5wFf44X0h2bLVRDsycgJmf9FF2elg+CrqD8GcFU8c6vYhgXn8NjUkFCwj+xDFb70qgLbTUm6sxwPmA==",
+ "license": "MIT",
+ "dependencies": {
+ "hsl-to-rgb-for-reals": "^1.1.0"
+ }
+ },
+ "node_modules/hsl-to-rgb-for-reals": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/hsl-to-rgb-for-reals/-/hsl-to-rgb-for-reals-1.1.1.tgz",
+ "integrity": "sha512-LgOWAkrN0rFaQpfdWBQlv/VhkOxb5AsBjk6NQVx4yEzWS923T07X0M1Y0VNko2H52HeSpZrZNNMJ0aFqsdVzQg==",
+ "license": "ISC"
+ },
"node_modules/husky": {
"version": "9.1.7",
"resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz",
@@ -4166,6 +4507,12 @@
"url": "https://github.com/sponsors/typicode"
}
},
+ "node_modules/hyphen": {
+ "version": "1.10.6",
+ "resolved": "https://registry.npmjs.org/hyphen/-/hyphen-1.10.6.tgz",
+ "integrity": "sha512-fXHXcGFTXOvZTSkPJuGOQf5Lv5T/R2itiiCVPg9LxAje5D00O0pP83yJShFq5V89Ly//Gt6acj7z8pbBr34stw==",
+ "license": "ISC"
+ },
"node_modules/ignore": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
@@ -4202,11 +4549,11 @@
"node": ">=0.8.19"
}
},
- "node_modules/inputmask": {
- "version": "5.0.9",
- "resolved": "https://registry.npmjs.org/inputmask/-/inputmask-5.0.9.tgz",
- "integrity": "sha512-s0lUfqcEbel+EQXtehXqwCJGShutgieOaIImFKC/r4reYNvX3foyrChl6LOEvaEgxEbesePIrw1Zi2jhZaDZbQ==",
- "license": "MIT"
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "license": "ISC"
},
"node_modules/internal-slot": {
"version": "1.1.0",
@@ -4584,6 +4931,12 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/is-url": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz",
+ "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==",
+ "license": "MIT"
+ },
"node_modules/is-weakmap": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz",
@@ -4662,6 +5015,15 @@
"node": ">= 0.4"
}
},
+ "node_modules/jay-peg": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/jay-peg/-/jay-peg-1.1.1.tgz",
+ "integrity": "sha512-D62KEuBxz/ip2gQKOEhk/mx14o7eiFRaU+VNNSP4MOiIkwb/D6B3G1Mfas7C/Fit8EsSV2/IWjZElx/Gs6A4ww==",
+ "license": "MIT",
+ "dependencies": {
+ "restructure": "^3.0.0"
+ }
+ },
"node_modules/jiti": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz",
@@ -4679,9 +5041,9 @@
"license": "MIT"
},
"node_modules/js-yaml": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
- "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
+ "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5064,6 +5426,25 @@
"url": "https://opencollective.com/parcel"
}
},
+ "node_modules/linebreak": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/linebreak/-/linebreak-1.1.0.tgz",
+ "integrity": "sha512-MHp03UImeVhB7XZtjd0E4n6+3xr5Dq/9xI/5FptGk5FrbDR3zagPa2DS6U8ks/3HjbKWG9Q1M2ufOzxV2qLYSQ==",
+ "license": "MIT",
+ "dependencies": {
+ "base64-js": "0.0.8",
+ "unicode-trie": "^2.0.0"
+ }
+ },
+ "node_modules/linebreak/node_modules/base64-js": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz",
+ "integrity": "sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/lines-and-columns": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
@@ -5136,6 +5517,12 @@
"node": ">= 0.4"
}
},
+ "node_modules/media-engine": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/media-engine/-/media-engine-1.0.3.tgz",
+ "integrity": "sha512-aa5tG6sDoK+k70B9iEX1NeyfT8ObCKhNDs6lJVpwF6r8vhUfuKMslIcirq6HIUYuuUYLefcEQOn9bSBOvawtwg==",
+ "license": "MIT"
+ },
"node_modules/memoize-one": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz",
@@ -5346,6 +5733,15 @@
"node": "^10 || ^12 || >=14"
}
},
+ "node_modules/normalize-svg-path": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/normalize-svg-path/-/normalize-svg-path-1.1.0.tgz",
+ "integrity": "sha512-r9KHKG2UUeB5LoTouwDzBy2VxXlHsiM6fyLQvnJa0S5hrhzqElH/CH7TUGhT1fVvIYBIKf3OpY4YJ4CK+iaqHg==",
+ "license": "MIT",
+ "dependencies": {
+ "svg-arc-to-cubic-bezier": "^3.0.0"
+ }
+ },
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@@ -5536,6 +5932,12 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/pako": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
+ "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
+ "license": "(MIT AND Zlib)"
+ },
"node_modules/parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -5566,6 +5968,12 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/parse-svg-path": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/parse-svg-path/-/parse-svg-path-0.1.2.tgz",
+ "integrity": "sha512-JyPSBnkTJ0AI8GGJLfMXvKq42cj5c006fnLz6fXy6zfoVjJizi8BNTpu8on8ziI1cKy9d9DGNuY17Ce7wuejpQ==",
+ "license": "MIT"
+ },
"node_modules/path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -5659,6 +6067,12 @@
"node": "^10 || ^12 || >=14"
}
},
+ "node_modules/postcss-value-parser": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
+ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
+ "license": "MIT"
+ },
"node_modules/prelude-ls": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@@ -5669,6 +6083,22 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/prettier": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz",
+ "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "prettier": "bin/prettier.cjs"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
"node_modules/prop-types": {
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
@@ -5702,6 +6132,15 @@
"node": ">=6"
}
},
+ "node_modules/queue": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz",
+ "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==",
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "~2.0.3"
+ }
+ },
"node_modules/queue-microtask": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
@@ -5732,6 +6171,27 @@
"node": ">=0.10.0"
}
},
+ "node_modules/react-day-picker": {
+ "version": "9.11.1",
+ "resolved": "https://registry.npmjs.org/react-day-picker/-/react-day-picker-9.11.1.tgz",
+ "integrity": "sha512-l3ub6o8NlchqIjPKrRFUCkTUEq6KwemQlfv3XZzzwpUeGwmDJ+0u0Upmt38hJyd7D/vn2dQoOoLV/qAp0o3uUw==",
+ "license": "MIT",
+ "dependencies": {
+ "@date-fns/tz": "^1.4.1",
+ "date-fns": "^4.1.0",
+ "date-fns-jalali": "^4.1.0-0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "individual",
+ "url": "https://github.com/sponsors/gpbl"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ }
+ },
"node_modules/react-dom": {
"version": "19.1.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz",
@@ -5744,6 +6204,23 @@
"react": "^19.1.0"
}
},
+ "node_modules/react-dropzone": {
+ "version": "14.3.8",
+ "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-14.3.8.tgz",
+ "integrity": "sha512-sBgODnq+lcA4P296DY4wacOZz3JFpD99fp+hb//iBO2HHnyeZU3FwWyXJ6salNpqQdsZrgMrotuko/BdJMV8Ug==",
+ "license": "MIT",
+ "dependencies": {
+ "attr-accept": "^2.2.4",
+ "file-selector": "^2.1.0",
+ "prop-types": "^15.8.1"
+ },
+ "engines": {
+ "node": ">= 10.13"
+ },
+ "peerDependencies": {
+ "react": ">= 16.8 || 18.0.0"
+ }
+ },
"node_modules/react-fast-compare": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz",
@@ -5870,6 +6347,15 @@
"integrity": "sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==",
"license": "MIT"
},
+ "node_modules/require-from-string": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/resolve": {
"version": "1.22.11",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz",
@@ -5909,6 +6395,12 @@
"url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
}
},
+ "node_modules/restructure": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/restructure/-/restructure-3.0.2.tgz",
+ "integrity": "sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw==",
+ "license": "MIT"
+ },
"node_modules/reusify": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
@@ -5964,6 +6456,26 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
"node_modules/safe-push-apply": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz",
@@ -6209,6 +6721,21 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/simple-swizzle": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz",
+ "integrity": "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==",
+ "license": "MIT",
+ "dependencies": {
+ "is-arrayish": "^0.3.1"
+ }
+ },
+ "node_modules/simple-swizzle/node_modules/is-arrayish": {
+ "version": "0.3.4",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz",
+ "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==",
+ "license": "MIT"
+ },
"node_modules/source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
@@ -6248,6 +6775,15 @@
"node": ">= 0.4"
}
},
+ "node_modules/string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "~5.2.0"
+ }
+ },
"node_modules/string.prototype.includes": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz",
@@ -6438,6 +6974,12 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/svg-arc-to-cubic-bezier": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/svg-arc-to-cubic-bezier/-/svg-arc-to-cubic-bezier-3.2.0.tgz",
+ "integrity": "sha512-djbJ/vZKZO+gPoSDThGNpKDO+o+bAeA4XQKovvkNCqnIS2t+S4qnLAGQhyyrulhCFRl1WWzAp0wUDV8PpTVU3g==",
+ "license": "ISC"
+ },
"node_modules/swr": {
"version": "2.3.6",
"resolved": "https://registry.npmjs.org/swr/-/swr-2.3.6.tgz",
@@ -6488,6 +7030,12 @@
"integrity": "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==",
"license": "MIT"
},
+ "node_modules/tiny-inflate": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz",
+ "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==",
+ "license": "MIT"
+ },
"node_modules/tiny-warning": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
@@ -6736,6 +7284,32 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/unicode-properties": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.4.1.tgz",
+ "integrity": "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==",
+ "license": "MIT",
+ "dependencies": {
+ "base64-js": "^1.3.0",
+ "unicode-trie": "^2.0.0"
+ }
+ },
+ "node_modules/unicode-trie": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz",
+ "integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==",
+ "license": "MIT",
+ "dependencies": {
+ "pako": "^0.2.5",
+ "tiny-inflate": "^1.0.0"
+ }
+ },
+ "node_modules/unicode-trie/node_modules/pako": {
+ "version": "0.2.9",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz",
+ "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==",
+ "license": "MIT"
+ },
"node_modules/unrs-resolver": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz",
@@ -6816,6 +7390,26 @@
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "license": "MIT"
+ },
+ "node_modules/vite-compatible-readable-stream": {
+ "version": "3.6.1",
+ "resolved": "https://registry.npmjs.org/vite-compatible-readable-stream/-/vite-compatible-readable-stream-3.6.1.tgz",
+ "integrity": "sha512-t20zYkrSf868+j/p31cRIGN28Phrjm3nRSLR2fyc2tiWi4cZGVdv68yNlwnIINTkMTmPoMiSlc0OadaO7DXZaQ==",
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -6953,6 +7547,12 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/yoga-layout": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/yoga-layout/-/yoga-layout-3.2.1.tgz",
+ "integrity": "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ==",
+ "license": "MIT"
+ },
"node_modules/yup": {
"version": "1.7.1",
"resolved": "https://registry.npmjs.org/yup/-/yup-1.7.1.tgz",
diff --git a/package.json b/package.json
index b371e4e7..7396d49d 100644
--- a/package.json
+++ b/package.json
@@ -7,19 +7,22 @@
"build": "next build --turbopack",
"start": "next start",
"lint": "eslint",
- "prepare": "husky"
+ "prepare": "husky",
+ "format": "prettier --write ."
},
"dependencies": {
+ "@react-pdf/renderer": "^4.3.1",
"@tanstack/match-sorter-utils": "^8.19.4",
"@tanstack/react-table": "^8.21.3",
"axios": "^1.12.2",
"clsx": "^2.1.1",
"formik": "^2.4.6",
- "inputmask": "^5.0.9",
"moment": "^2.30.1",
"next": "15.5.3",
"react": "19.1.0",
+ "react-day-picker": "^9.11.1",
"react-dom": "19.1.0",
+ "react-dropzone": "^14.3.8",
"react-hot-toast": "^2.6.0",
"react-number-format": "^5.4.4",
"react-select": "^5.10.2",
@@ -33,7 +36,6 @@
"@eslint/eslintrc": "^3",
"@iconify/react": "^6.0.2",
"@tailwindcss/postcss": "^4",
- "@types/inputmask": "^5.0.7",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
@@ -41,6 +43,7 @@
"eslint": "^9",
"eslint-config-next": "15.5.3",
"husky": "^9.1.7",
+ "prettier": "^3.6.2",
"tailwindcss": "^4",
"typescript": "^5"
}
diff --git a/postcss.config.mjs b/postcss.config.mjs
index c7bcb4b1..ba720fe5 100644
--- a/postcss.config.mjs
+++ b/postcss.config.mjs
@@ -1,5 +1,5 @@
const config = {
- plugins: ["@tailwindcss/postcss"],
+ plugins: ['@tailwindcss/postcss'],
};
export default config;
diff --git a/src/app/expense/add/page.tsx b/src/app/expense/add/page.tsx
new file mode 100644
index 00000000..afa40f48
--- /dev/null
+++ b/src/app/expense/add/page.tsx
@@ -0,0 +1,11 @@
+import ExpenseRequestForm from '@/components/pages/expense/form/ExpenseRequestForm';
+
+const AddExpense = () => {
+ return (
+
+
+
+ );
+};
+
+export default AddExpense;
diff --git a/src/app/expense/detail/edit/page.tsx b/src/app/expense/detail/edit/page.tsx
new file mode 100644
index 00000000..b37fdb8f
--- /dev/null
+++ b/src/app/expense/detail/edit/page.tsx
@@ -0,0 +1,61 @@
+'use client';
+
+import { useRouter, useSearchParams } from 'next/navigation';
+import useSWR from 'swr';
+
+import ExpenseRequestForm from '@/components/pages/expense/form/ExpenseRequestForm';
+
+import { ExpenseApi } from '@/services/api/expense';
+import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
+
+const ExpenseEditPage = () => {
+ const router = useRouter();
+ const searchParams = useSearchParams();
+
+ const expenseId = searchParams.get('expenseId');
+
+ const { data: expense, isLoading: isLoadingExpense } = useSWR(
+ expenseId,
+ (id: number) => ExpenseApi.getSingle(id)
+ );
+
+ if (!expenseId) {
+ router.back();
+
+ return (
+
+
+
+ );
+ }
+
+ if (!isLoadingExpense && (!expense || isResponseError(expense))) {
+ router.replace('/404');
+ return;
+ }
+
+ const isExpenseRejectedOrApproved =
+ !isLoadingExpense &&
+ isResponseSuccess(expense) &&
+ (expense.data.approval.action === 'REJECTED' ||
+ expense.data.approval.step_number === 5);
+
+ if (isExpenseRejectedOrApproved) {
+ router.back();
+ return;
+ }
+
+ return (
+
+ {isLoadingExpense && (
+
+ )}
+
+ {!isLoadingExpense && isResponseSuccess(expense) && (
+
+ )}
+
+ );
+};
+
+export default ExpenseEditPage;
diff --git a/src/app/expense/detail/layout.tsx b/src/app/expense/detail/layout.tsx
new file mode 100644
index 00000000..7220dfa1
--- /dev/null
+++ b/src/app/expense/detail/layout.tsx
@@ -0,0 +1,11 @@
+import SuspenseHelper from '@/components/helper/SuspenseHelper';
+
+const Layout = ({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) => {
+ return {children};
+};
+
+export default Layout;
diff --git a/src/app/expense/detail/page.tsx b/src/app/expense/detail/page.tsx
new file mode 100644
index 00000000..a0d90f70
--- /dev/null
+++ b/src/app/expense/detail/page.tsx
@@ -0,0 +1,50 @@
+'use client';
+
+import { useRouter, useSearchParams } from 'next/navigation';
+import useSWR from 'swr';
+
+import ExpenseDetail from '@/components/pages/expense/ExpenseDetail';
+
+import { ExpenseApi } from '@/services/api/expense';
+import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
+
+const ExpenseDetailPage = () => {
+ const router = useRouter();
+ const searchParams = useSearchParams();
+
+ const expenseId = searchParams.get('expenseId');
+
+ const { data: expense, isLoading: isLoadingExpense } = useSWR(
+ expenseId,
+ (id: number) => ExpenseApi.getSingle(id)
+ );
+
+ if (!expenseId) {
+ router.back();
+
+ return (
+
+
+
+ );
+ }
+
+ if (!isLoadingExpense && (!expense || isResponseError(expense))) {
+ router.replace('/404');
+ return;
+ }
+
+ return (
+
+ {isLoadingExpense && (
+
+ )}
+
+ {!isLoadingExpense && isResponseSuccess(expense) && (
+
+ )}
+
+ );
+};
+
+export default ExpenseDetailPage;
diff --git a/src/app/expense/page.tsx b/src/app/expense/page.tsx
new file mode 100644
index 00000000..d6b00286
--- /dev/null
+++ b/src/app/expense/page.tsx
@@ -0,0 +1,11 @@
+import ExpensesTable from '@/components/pages/expense/ExpensesTable';
+
+const Expense = () => {
+ return (
+
+ );
+};
+
+export default Expense;
diff --git a/src/app/globals.css b/src/app/globals.css
index 97be6978..e50e020d 100644
--- a/src/app/globals.css
+++ b/src/app/globals.css
@@ -3,10 +3,10 @@
@import '../styles/daisyui.css';
@plugin "daisyui/theme" {
- name: "lti";
+ name: 'lti';
default: false;
prefersdark: false;
- color-scheme: "light";
+ color-scheme: 'light';
--color-base-100: oklch(98% 0.001 106.423);
--color-base-200: oklch(97% 0.001 106.424);
--color-base-300: oklch(92% 0.003 48.717);
@@ -37,8 +37,6 @@
--noise: 0;
}
-
-
:root {
--color-primary: #1f74bf;
}
@@ -50,3 +48,8 @@
html {
scrollbar-gutter: initial;
}
+
+.react-select__menu-portal {
+ position: relative;
+ z-index: 99999 !important;
+}
diff --git a/src/app/inventory/adjustment/add/page.tsx b/src/app/inventory/adjustment/add/page.tsx
index 3bd64573..e20eedfc 100644
--- a/src/app/inventory/adjustment/add/page.tsx
+++ b/src/app/inventory/adjustment/add/page.tsx
@@ -1,11 +1,11 @@
-import InventoryAdjustmentForm from "@/components/pages/inventory/adjustment/form/InventoryAdjustmentForm";
+import InventoryAdjustmentForm from '@/components/pages/inventory/adjustment/form/InventoryAdjustmentForm';
const CreateInventoryAdjustment = () => {
return (
-
-
+
);
-}
+};
-export default CreateInventoryAdjustment;
\ No newline at end of file
+export default CreateInventoryAdjustment;
diff --git a/src/app/inventory/adjustment/detail/layout.tsx b/src/app/inventory/adjustment/detail/layout.tsx
index b41c70f9..7220dfa1 100644
--- a/src/app/inventory/adjustment/detail/layout.tsx
+++ b/src/app/inventory/adjustment/detail/layout.tsx
@@ -1,11 +1,11 @@
-import SuspenseHelper from "@/components/helper/SuspenseHelper"
+import SuspenseHelper from '@/components/helper/SuspenseHelper';
const Layout = ({
- children
+ children,
}: Readonly<{
- children: React.ReactNode
+ children: React.ReactNode;
}>) => {
- return {children}
-}
+ return {children};
+};
-export default Layout;
\ No newline at end of file
+export default Layout;
diff --git a/src/app/inventory/adjustment/detail/page.tsx b/src/app/inventory/adjustment/detail/page.tsx
index 5e96c86a..acb9f8db 100644
--- a/src/app/inventory/adjustment/detail/page.tsx
+++ b/src/app/inventory/adjustment/detail/page.tsx
@@ -7,11 +7,12 @@ import type { InventoryAdjustment } from '@/types/api/inventory/adjustment';
const DetailInventoryAdjustment = () => {
const router = useRouter();
- const [inventoryAdjustment, setInventoryAdjustment] = useState(null);
+ const [inventoryAdjustment, setInventoryAdjustment] =
+ useState(null);
// Ambil data dari router state
useEffect(() => {
- console.log("Router State");
+ console.log('Router State');
console.log(window.history.state);
const state = window.history.state?.usr as
| { inventoryAdjustment?: InventoryAdjustment }
@@ -24,20 +25,20 @@ const DetailInventoryAdjustment = () => {
}, [router]);
const finalData = inventoryAdjustment;
-
- console.log("Final Data");
+
+ console.log('Final Data');
console.log(finalData);
if (!finalData) {
return (
-
-
+
+
);
}
return (
-
+
);
diff --git a/src/app/marketing/add/delivery-orders/layout.tsx b/src/app/marketing/add/delivery-orders/layout.tsx
new file mode 100644
index 00000000..7220dfa1
--- /dev/null
+++ b/src/app/marketing/add/delivery-orders/layout.tsx
@@ -0,0 +1,11 @@
+import SuspenseHelper from '@/components/helper/SuspenseHelper';
+
+const Layout = ({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) => {
+ return {children};
+};
+
+export default Layout;
diff --git a/src/app/marketing/add/delivery-orders/page.tsx b/src/app/marketing/add/delivery-orders/page.tsx
new file mode 100644
index 00000000..4d92acda
--- /dev/null
+++ b/src/app/marketing/add/delivery-orders/page.tsx
@@ -0,0 +1,54 @@
+'use client';
+
+import MarketingForm from '@/components/pages/marketing/form/MarketingForm';
+import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
+import { MarketingApi } from '@/services/api/marketing/marketing';
+import { useRouter, useSearchParams } from 'next/navigation';
+import toast from 'react-hot-toast';
+import useSWR from 'swr';
+
+const EditMarketingDelivery = () => {
+ const router = useRouter();
+ const searchParams = useSearchParams();
+
+ const soId = searchParams.get('marketingId');
+
+ const {
+ data: marketing,
+ isLoading: isLoading,
+ mutate: refreshMarketing,
+ } = useSWR(`get-so-${soId}`, () =>
+ MarketingApi.getSingle(soId ? parseInt(soId) : 0)
+ );
+
+ if (!soId) {
+ router.back();
+
+ return (
+
+
+
+ );
+ }
+
+ if (!isLoading && (!marketing || isResponseError(marketing))) {
+ router.replace('/404');
+ return;
+ }
+
+ return (
+
+ {isLoading && }
+ {!isLoading && isResponseSuccess(marketing) && (
+ {
+ refreshMarketing();
+ }}
+ />
+ )}
+
+ );
+};
+export default EditMarketingDelivery;
diff --git a/src/app/marketing/add/sales-orders/page.tsx b/src/app/marketing/add/sales-orders/page.tsx
new file mode 100644
index 00000000..9e33d304
--- /dev/null
+++ b/src/app/marketing/add/sales-orders/page.tsx
@@ -0,0 +1,11 @@
+import MarketingForm from '@/components/pages/marketing/form/MarketingForm';
+
+const AddSalesOrder = () => {
+ return (
+
+
+
+ );
+};
+
+export default AddSalesOrder;
diff --git a/src/app/marketing/detail/delivery-orders/edit/layout.tsx b/src/app/marketing/detail/delivery-orders/edit/layout.tsx
new file mode 100644
index 00000000..7220dfa1
--- /dev/null
+++ b/src/app/marketing/detail/delivery-orders/edit/layout.tsx
@@ -0,0 +1,11 @@
+import SuspenseHelper from '@/components/helper/SuspenseHelper';
+
+const Layout = ({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) => {
+ return {children};
+};
+
+export default Layout;
diff --git a/src/app/marketing/detail/delivery-orders/edit/page.tsx b/src/app/marketing/detail/delivery-orders/edit/page.tsx
new file mode 100644
index 00000000..32625026
--- /dev/null
+++ b/src/app/marketing/detail/delivery-orders/edit/page.tsx
@@ -0,0 +1,62 @@
+'use client';
+
+import MarketingForm from '@/components/pages/marketing/form/MarketingForm';
+import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
+import { MarketingApi } from '@/services/api/marketing/marketing';
+import { useRouter, useSearchParams } from 'next/navigation';
+import toast from 'react-hot-toast';
+import useSWR from 'swr';
+
+const EditMarketingDelivery = () => {
+ const router = useRouter();
+ const searchParams = useSearchParams();
+
+ const soId = searchParams.get('marketingId');
+
+ const {
+ data: marketing,
+ isLoading: isLoading,
+ mutate: refreshMarketing,
+ } = useSWR(`get-so-${soId}`, () =>
+ MarketingApi.getSingle(soId ? parseInt(soId) : 0)
+ );
+
+ if (!soId) {
+ router.back();
+
+ return (
+
+
+
+ );
+ }
+
+ if (!isLoading && (!marketing || isResponseError(marketing))) {
+ router.replace('/404');
+ return;
+ }
+
+ if (
+ isResponseSuccess(marketing) &&
+ marketing.data.latest_approval.step_number != 3
+ ) {
+ toast.error('Data Marketing perlu dilakukan approval terlebih dahulu!');
+ router.back();
+ }
+
+ return (
+
+ {isLoading && }
+ {!isLoading && isResponseSuccess(marketing) && (
+ {
+ refreshMarketing();
+ }}
+ />
+ )}
+
+ );
+};
+export default EditMarketingDelivery;
diff --git a/src/app/marketing/detail/layout.tsx b/src/app/marketing/detail/layout.tsx
new file mode 100644
index 00000000..7220dfa1
--- /dev/null
+++ b/src/app/marketing/detail/layout.tsx
@@ -0,0 +1,11 @@
+import SuspenseHelper from '@/components/helper/SuspenseHelper';
+
+const Layout = ({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) => {
+ return {children};
+};
+
+export default Layout;
diff --git a/src/app/marketing/detail/page.tsx b/src/app/marketing/detail/page.tsx
new file mode 100644
index 00000000..902251e8
--- /dev/null
+++ b/src/app/marketing/detail/page.tsx
@@ -0,0 +1,49 @@
+'use client';
+
+import MarketingDetail from '@/components/pages/marketing/detail/MarketingDetail';
+import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
+import { MarketingApi } from '@/services/api/marketing/marketing';
+import { useRouter, useSearchParams } from 'next/navigation';
+import useSWR from 'swr';
+
+const DetailMarketing = () => {
+ const router = useRouter();
+ const searchParams = useSearchParams();
+
+ const soId = searchParams.get('marketingId');
+
+ const {
+ data: marketing,
+ isLoading: isLoading,
+ mutate: refreshMarketing,
+ } = useSWR(soId, (id: number) => MarketingApi.getSingle(id));
+
+ if (!soId) {
+ router.back();
+
+ return (
+
+
+
+ );
+ }
+
+ if (!isLoading && (!marketing || isResponseError(marketing))) {
+ router.replace('/404');
+ return;
+ }
+
+ return (
+
+ {isLoading && }
+ {!isLoading && isResponseSuccess(marketing) && (
+
+ )}
+
+ );
+};
+
+export default DetailMarketing;
diff --git a/src/app/marketing/detail/sales-orders/edit/layout.tsx b/src/app/marketing/detail/sales-orders/edit/layout.tsx
new file mode 100644
index 00000000..7220dfa1
--- /dev/null
+++ b/src/app/marketing/detail/sales-orders/edit/layout.tsx
@@ -0,0 +1,11 @@
+import SuspenseHelper from '@/components/helper/SuspenseHelper';
+
+const Layout = ({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) => {
+ return {children};
+};
+
+export default Layout;
diff --git a/src/app/marketing/detail/sales-orders/edit/page.tsx b/src/app/marketing/detail/sales-orders/edit/page.tsx
new file mode 100644
index 00000000..19a098c5
--- /dev/null
+++ b/src/app/marketing/detail/sales-orders/edit/page.tsx
@@ -0,0 +1,52 @@
+'use client';
+
+import MarketingForm from '@/components/pages/marketing/form/MarketingForm';
+import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
+import { MarketingApi } from '@/services/api/marketing/marketing';
+import { useRouter, useSearchParams } from 'next/navigation';
+import useSWR from 'swr';
+
+const EditSalesOrder = () => {
+ const router = useRouter();
+ const searchParams = useSearchParams();
+
+ const soId = searchParams.get('marketingId');
+
+ const {
+ data: marketing,
+ isLoading: isLoading,
+ mutate: refreshMarketing,
+ } = useSWR(`get-so-${soId}`, () =>
+ MarketingApi.getSingle(soId ? parseInt(soId) : 0)
+ );
+
+ if (!soId) {
+ router.back();
+
+ return (
+
+
+
+ );
+ }
+
+ if (!isLoading && (!marketing || isResponseError(marketing))) {
+ router.replace('/404');
+ return;
+ }
+ return (
+
+ {isLoading && }
+ {!isLoading && isResponseSuccess(marketing) && (
+ {
+ refreshMarketing();
+ }}
+ />
+ )}
+
+ );
+};
+export default EditSalesOrder;
diff --git a/src/app/marketing/page.tsx b/src/app/marketing/page.tsx
new file mode 100644
index 00000000..99a80b64
--- /dev/null
+++ b/src/app/marketing/page.tsx
@@ -0,0 +1,10 @@
+import MarketingTable from '@/components/pages/marketing/MarketingTable';
+
+const Marketing = () => {
+ return (
+
+
+
+ );
+};
+export default Marketing;
diff --git a/src/app/master-data/customer/add/page.tsx b/src/app/master-data/customer/add/page.tsx
index a1096f02..dd75c679 100644
--- a/src/app/master-data/customer/add/page.tsx
+++ b/src/app/master-data/customer/add/page.tsx
@@ -1,11 +1,11 @@
-import CustomerForm from "@/components/pages/master-data/customer/form/CustomerForm";
+import CustomerForm from '@/components/pages/master-data/customer/form/CustomerForm';
const AddCustomer = () => {
return (
-
-
+
);
-}
+};
-export default AddCustomer;
\ No newline at end of file
+export default AddCustomer;
diff --git a/src/app/master-data/customer/detail/page.tsx b/src/app/master-data/customer/detail/page.tsx
index 263458c2..d778f83b 100644
--- a/src/app/master-data/customer/detail/page.tsx
+++ b/src/app/master-data/customer/detail/page.tsx
@@ -1,45 +1,47 @@
-'use client'
+'use client';
-import { useRouter, useSearchParams } from "next/navigation";
-import useSWR from "swr";
+import { useRouter, useSearchParams } from 'next/navigation';
+import useSWR from 'swr';
import { CustomerApi } from '@/services/api/master-data';
-import { isResponseError, isResponseSuccess } from "@/lib/api-helper";
-import CustomerForm from "@/components/pages/master-data/customer/form/CustomerForm";
+import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
+import CustomerForm from '@/components/pages/master-data/customer/form/CustomerForm';
const CustomerDetail = () => {
const router = useRouter();
const searchParams = useSearchParams();
- const costumerId = searchParams.get("customerId");
+ const costumerId = searchParams.get('customerId');
const { data: costumer, isLoading: isLoadingCostumer } = useSWR(
costumerId,
(id: number) => CustomerApi.getSingle(id)
);
- if(!costumerId){
+ if (!costumerId) {
router.back();
return (
-
-
+
+
);
}
- if(!isLoadingCostumer && (!costumer || isResponseError(costumer))){
- router.replace("/404");
+ if (!isLoadingCostumer && (!costumer || isResponseError(costumer))) {
+ router.replace('/404');
return;
}
return (
-
- {isLoadingCostumer &&
}
+
+ {isLoadingCostumer && (
+
+ )}
{!isLoadingCostumer && isResponseSuccess(costumer) && (
-
+
)}
- )
+ );
};
export default CustomerDetail;
diff --git a/src/app/master-data/customer/page.tsx b/src/app/master-data/customer/page.tsx
index b80401f1..8aec1088 100644
--- a/src/app/master-data/customer/page.tsx
+++ b/src/app/master-data/customer/page.tsx
@@ -1,11 +1,11 @@
-import CustomersTable from "@/components/pages/master-data/customer/CustomersTable";
+import CustomersTable from '@/components/pages/master-data/customer/CustomersTable';
const Customer = () => {
return (
-
+
- )
+ );
};
-export default Customer;
\ No newline at end of file
+export default Customer;
diff --git a/src/app/master-data/flock/add/page.tsx b/src/app/master-data/flock/add/page.tsx
index 5ee3958e..d038d414 100644
--- a/src/app/master-data/flock/add/page.tsx
+++ b/src/app/master-data/flock/add/page.tsx
@@ -1,11 +1,11 @@
-import FlockForm from "@/components/pages/master-data/flock/form/FlockForm";
+import FlockForm from '@/components/pages/master-data/flock/form/FlockForm';
const AddFlock = () => {
return (
-
+
);
-}
+};
export default AddFlock;
diff --git a/src/app/master-data/flock/detail/edit/page.tsx b/src/app/master-data/flock/detail/edit/page.tsx
index c9651727..babc6653 100644
--- a/src/app/master-data/flock/detail/edit/page.tsx
+++ b/src/app/master-data/flock/detail/edit/page.tsx
@@ -1,10 +1,10 @@
-'use client'
+'use client';
-import FlockForm from "@/components/pages/master-data/flock/form/FlockForm";
-import { isResponseError, isResponseSuccess } from "@/lib/api-helper";
-import { FlockApi } from "@/services/api/master-data";
-import { useRouter, useSearchParams } from "next/navigation";
-import useSWR from "swr";
+import FlockForm from '@/components/pages/master-data/flock/form/FlockForm';
+import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
+import { FlockApi } from '@/services/api/master-data';
+import { useRouter, useSearchParams } from 'next/navigation';
+import useSWR from 'swr';
const FlockEdit = () => {
const router = useRouter();
@@ -44,6 +44,6 @@ const FlockEdit = () => {
)}
);
-}
+};
-export default FlockEdit;
\ No newline at end of file
+export default FlockEdit;
diff --git a/src/app/master-data/flock/detail/layout.tsx b/src/app/master-data/flock/detail/layout.tsx
index b41c70f9..7220dfa1 100644
--- a/src/app/master-data/flock/detail/layout.tsx
+++ b/src/app/master-data/flock/detail/layout.tsx
@@ -1,11 +1,11 @@
-import SuspenseHelper from "@/components/helper/SuspenseHelper"
+import SuspenseHelper from '@/components/helper/SuspenseHelper';
const Layout = ({
- children
+ children,
}: Readonly<{
- children: React.ReactNode
+ children: React.ReactNode;
}>) => {
- return
{children}
-}
+ return
{children};
+};
-export default Layout;
\ No newline at end of file
+export default Layout;
diff --git a/src/app/master-data/flock/detail/page.tsx b/src/app/master-data/flock/detail/page.tsx
index 8a805911..e9620d33 100644
--- a/src/app/master-data/flock/detail/page.tsx
+++ b/src/app/master-data/flock/detail/page.tsx
@@ -1,10 +1,10 @@
-'use client'
+'use client';
-import FlockForm from "@/components/pages/master-data/flock/form/FlockForm";
-import { isResponseError, isResponseSuccess } from "@/lib/api-helper";
-import { FlockApi } from "@/services/api/master-data";
-import { useRouter, useSearchParams } from "next/navigation";
-import useSWR from "swr";
+import FlockForm from '@/components/pages/master-data/flock/form/FlockForm';
+import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
+import { FlockApi } from '@/services/api/master-data';
+import { useRouter, useSearchParams } from 'next/navigation';
+import useSWR from 'swr';
const FlockDetail = () => {
const router = useRouter();
@@ -14,33 +14,36 @@ const FlockDetail = () => {
const flockId = searchParams.get('flockId');
// Fetch Data
- const { data: flock, isLoading: isLoadingFlock } = useSWR(flockId, (id: number) => FlockApi.getSingle(id));
+ const { data: flock, isLoading: isLoadingFlock } = useSWR(
+ flockId,
+ (id: number) => FlockApi.getSingle(id)
+ );
- if(!flockId){
+ if (!flockId) {
router.back();
return (
-
-
+
+
);
}
- if(!isLoadingFlock && (!flock || isResponseError(flock))){
+ if (!isLoadingFlock && (!flock || isResponseError(flock))) {
router.replace('/404');
return;
}
return (
-
+
{isLoadingFlock && (
-
+
)}
{!isLoadingFlock && isResponseSuccess(flock) && (
-
+
)}
);
-}
+};
-export default FlockDetail;
\ No newline at end of file
+export default FlockDetail;
diff --git a/src/app/master-data/flock/page.tsx b/src/app/master-data/flock/page.tsx
index b317091a..76cc32c1 100644
--- a/src/app/master-data/flock/page.tsx
+++ b/src/app/master-data/flock/page.tsx
@@ -1,11 +1,11 @@
-import FlockTable from "@/components/pages/master-data/flock/FlocksTable";
+import FlockTable from '@/components/pages/master-data/flock/FlocksTable';
const Flock = () => {
return (
-
-
+
- );
-}
+ );
+};
export default Flock;
diff --git a/src/app/master-data/product-category/add/page.tsx b/src/app/master-data/product-category/add/page.tsx
index 0993ba7a..2331159e 100644
--- a/src/app/master-data/product-category/add/page.tsx
+++ b/src/app/master-data/product-category/add/page.tsx
@@ -1,11 +1,11 @@
-import ProductCategoryForm from "@/components/pages/master-data/product-category/form/ProductCategoryForm";
+import ProductCategoryForm from '@/components/pages/master-data/product-category/form/ProductCategoryForm';
const AddProductCategory = () => {
return (
-
+
);
};
-export default AddProductCategory;
\ No newline at end of file
+export default AddProductCategory;
diff --git a/src/app/master-data/product-category/detail/edit/page.tsx b/src/app/master-data/product-category/detail/edit/page.tsx
index 6bc10644..4cb7eb5a 100644
--- a/src/app/master-data/product-category/detail/edit/page.tsx
+++ b/src/app/master-data/product-category/detail/edit/page.tsx
@@ -9,39 +9,44 @@ import { ProductCategoryApi } from '@/services/api/master-data';
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
const ProductCategoryEdit = () => {
- const router = useRouter();
- const searchParams = useSearchParams();
+ const router = useRouter();
+ const searchParams = useSearchParams();
- const productCategoryId = searchParams.get('productCategoryId');
+ const productCategoryId = searchParams.get('productCategoryId');
- const { data: productCategory, isLoading: isLoadingProductCategory } = useSWR(
- productCategoryId,
- (id: number) => ProductCategoryApi.getSingle(id)
- );
+ const { data: productCategory, isLoading: isLoadingProductCategory } = useSWR(
+ productCategoryId,
+ (id: number) => ProductCategoryApi.getSingle(id)
+ );
- if (!productCategoryId) {
- router.back();
-
- return (
-
-
-
- );
- }
-
- if (!isLoadingProductCategory && (!productCategory || isResponseError(productCategory))) {
- router.replace('/404');
- return;
- }
+ if (!productCategoryId) {
+ router.back();
return (
-
- {isLoadingProductCategory &&
}
- {!isLoadingProductCategory && isResponseSuccess(productCategory) && (
-
- )}
-
+
+
+
);
-}
+ }
-export default ProductCategoryEdit;
\ No newline at end of file
+ if (
+ !isLoadingProductCategory &&
+ (!productCategory || isResponseError(productCategory))
+ ) {
+ router.replace('/404');
+ return;
+ }
+
+ return (
+
+ {isLoadingProductCategory && (
+
+ )}
+ {!isLoadingProductCategory && isResponseSuccess(productCategory) && (
+
+ )}
+
+ );
+};
+
+export default ProductCategoryEdit;
diff --git a/src/app/master-data/product-category/detail/page.tsx b/src/app/master-data/product-category/detail/page.tsx
index cba06fdb..c1a21aaf 100644
--- a/src/app/master-data/product-category/detail/page.tsx
+++ b/src/app/master-data/product-category/detail/page.tsx
@@ -29,16 +29,24 @@ const ProductCategoryDetail = () => {
);
}
- if (!isLoadingProductCategory && (!productCategory || isResponseError(productCategory))) {
+ if (
+ !isLoadingProductCategory &&
+ (!productCategory || isResponseError(productCategory))
+ ) {
router.replace('/404');
return;
}
return (
- {isLoadingProductCategory &&
}
+ {isLoadingProductCategory && (
+
+ )}
{!isLoadingProductCategory && isResponseSuccess(productCategory) && (
-
+
)}
);
diff --git a/src/app/master-data/product-category/page.tsx b/src/app/master-data/product-category/page.tsx
index 5ec6d555..78a4fda3 100644
--- a/src/app/master-data/product-category/page.tsx
+++ b/src/app/master-data/product-category/page.tsx
@@ -1,11 +1,11 @@
-import ProductCategoryTable from "@/components/pages/master-data/product-category/ProductCategoryTable";
+import ProductCategoryTable from '@/components/pages/master-data/product-category/ProductCategoryTable';
const ProductCategory = () => {
return (
-
+
);
};
-export default ProductCategory;
\ No newline at end of file
+export default ProductCategory;
diff --git a/src/app/master-data/product/add/page.tsx b/src/app/master-data/product/add/page.tsx
index 7cc995b6..37f42691 100644
--- a/src/app/master-data/product/add/page.tsx
+++ b/src/app/master-data/product/add/page.tsx
@@ -2,10 +2,10 @@ import ProductForm from '@/components/pages/master-data/product/form/ProductForm
const AddProduct = () => {
return (
-
+
);
};
-export default AddProduct;
\ No newline at end of file
+export default AddProduct;
diff --git a/src/app/master-data/product/detail/edit/page.tsx b/src/app/master-data/product/detail/edit/page.tsx
index 96cfdc42..8916a98e 100644
--- a/src/app/master-data/product/detail/edit/page.tsx
+++ b/src/app/master-data/product/detail/edit/page.tsx
@@ -13,9 +13,8 @@ const ProductEdit = () => {
const productId = searchParams.get('productId');
- const { data: product, isLoading } = useSWR(
- productId,
- (id: number) => ProductApi.getSingle(id)
+ const { data: product, isLoading } = useSWR(productId, (id: number) =>
+ ProductApi.getSingle(id)
);
if (!productId) {
@@ -42,4 +41,4 @@ const ProductEdit = () => {
);
};
-export default ProductEdit;
\ No newline at end of file
+export default ProductEdit;
diff --git a/src/app/master-data/product/detail/page.tsx b/src/app/master-data/product/detail/page.tsx
index 916a44d0..34743e1f 100644
--- a/src/app/master-data/product/detail/page.tsx
+++ b/src/app/master-data/product/detail/page.tsx
@@ -13,9 +13,8 @@ const ProductDetail = () => {
const productId = searchParams.get('productId');
- const { data: product, isLoading } = useSWR(
- productId,
- (id: number) => ProductApi.getSingle(id)
+ const { data: product, isLoading } = useSWR(productId, (id: number) =>
+ ProductApi.getSingle(id)
);
if (!productId) {
@@ -42,4 +41,4 @@ const ProductDetail = () => {
);
};
-export default ProductDetail;
\ No newline at end of file
+export default ProductDetail;
diff --git a/src/app/master-data/product/page.tsx b/src/app/master-data/product/page.tsx
index 6014aeb9..a385d411 100644
--- a/src/app/master-data/product/page.tsx
+++ b/src/app/master-data/product/page.tsx
@@ -1,11 +1,11 @@
-import ProductsTable from "@/components/pages/master-data/product/ProductTable";
+import ProductsTable from '@/components/pages/master-data/product/ProductTable';
const Product = () => {
return (
-
-
+
);
};
-export default Product;
\ No newline at end of file
+export default Product;
diff --git a/src/app/master-data/supplier/add/page.tsx b/src/app/master-data/supplier/add/page.tsx
index 8a95c3c6..37df33b0 100644
--- a/src/app/master-data/supplier/add/page.tsx
+++ b/src/app/master-data/supplier/add/page.tsx
@@ -8,4 +8,4 @@ const AddSupplier = () => {
);
};
-export default AddSupplier;
\ No newline at end of file
+export default AddSupplier;
diff --git a/src/app/master-data/supplier/detail/page.tsx b/src/app/master-data/supplier/detail/page.tsx
index 433fa043..a34ad72e 100644
--- a/src/app/master-data/supplier/detail/page.tsx
+++ b/src/app/master-data/supplier/detail/page.tsx
@@ -46,4 +46,4 @@ const SupplierDetail = () => {
);
};
-export default SupplierDetail;
\ No newline at end of file
+export default SupplierDetail;
diff --git a/src/app/master-data/supplier/page.tsx b/src/app/master-data/supplier/page.tsx
index 1f54bd0d..8000be0a 100644
--- a/src/app/master-data/supplier/page.tsx
+++ b/src/app/master-data/supplier/page.tsx
@@ -1,4 +1,4 @@
-import SuppliersTable from "@/components/pages/master-data/supplier/SupplierTable";
+import SuppliersTable from '@/components/pages/master-data/supplier/SupplierTable';
const Supplier = () => {
return (
diff --git a/src/app/production/chickin/add/layout.tsx b/src/app/production/chickin/add/layout.tsx
deleted file mode 100644
index b41c70f9..00000000
--- a/src/app/production/chickin/add/layout.tsx
+++ /dev/null
@@ -1,11 +0,0 @@
-import SuspenseHelper from "@/components/helper/SuspenseHelper"
-
-const Layout = ({
- children
-}: Readonly<{
- children: React.ReactNode
-}>) => {
- return {children}
-}
-
-export default Layout;
\ No newline at end of file
diff --git a/src/app/production/chickin/add/page.tsx b/src/app/production/chickin/add/page.tsx
deleted file mode 100644
index 3ef73396..00000000
--- a/src/app/production/chickin/add/page.tsx
+++ /dev/null
@@ -1,270 +0,0 @@
-'use client';
-
-import Button from '@/components/Button';
-import SelectInput, { OptionType } from '@/components/input/SelectInput';
-import Modal, { useModal } from '@/components/Modal';
-import ConfirmationModal from '@/components/modal/ConfirmationModal';
-import ChickinForm from '@/components/pages/production/chickin/form/ChickinForm';
-import Table from '@/components/Table';
-import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
-import { cn } from '@/lib/helper';
-import { ProjectFlockApi } from '@/services/api/production';
-import { useTableFilter } from '@/services/hooks/useTableFilter';
-import { BaseApiResponse } from '@/types/api/api-general';
-import { Kandang } from '@/types/api/master-data/kandang';
-import { ProjectFlockKandang } from '@/types/api/production/project-flock-kandang';
-import { Icon } from '@iconify/react';
-import { useRouter, useSearchParams } from 'next/navigation';
-import { useState } from 'react';
-
-import useSWR from 'swr';
-
-const AddChickin = () => {
- const router = useRouter();
- const searchParams = useSearchParams();
- const projectFlockId = searchParams.get('projectFlockId');
-
- // Tables Props
- const { state: tableFilterState } = useTableFilter({
- initial: { search: '' },
- paramMap: { page: 'page', pageSize: 'limit' },
- });
-
- // States
- const [selectedKandang, setSelectedKandang] = useState(
- undefined
- );
- const [projectFlockKandang, setProjectFlockKandang] =
- useState>();
- const [isLoadingProjectFlockKandang, setIsLoadingProjectFlockKandang] =
- useState(false);
- const [searchProjectFlock, setSearchProjectFlock] = useState('');
-
- // Fetch Data
- const { data: projectFlock, isLoading: isLoadingProjectFlock } = useSWR(
- projectFlockId,
- (id: number) => ProjectFlockApi.getSingle(id)
- );
- const { data: listProjectFlock, isLoading: isLoadingListProjectFlock } =
- useSWR(
- `${ProjectFlockApi.basePath}?${new URLSearchParams({
- search: searchProjectFlock,
- }).toString()}`,
- ProjectFlockApi.getAllFetcher
- );
-
- const getProjectFlockKandangUrl = `/kandangs/lookup`;
- // Mapping Options
- const options = isResponseSuccess(listProjectFlock)
- ? listProjectFlock?.data.map((projectFlock) => {
- return {
- value: projectFlock.id,
- label: `${projectFlock?.flock?.name} - ${projectFlock?.category} - Periode ${projectFlock.period}`,
- };
- })
- : [];
-
- const chickinModal = useModal();
- const alertModal = useModal();
-
- if (!projectFlockId) {
- router.back();
-
- return (
-
-
-
- );
- }
-
- if (
- !isLoadingProjectFlock &&
- (!projectFlock || isResponseError(projectFlock))
- ) {
- router.replace('/404');
- return;
- }
-
- // Handle Function
- const handleChickinClick = async (kandang: Kandang) => {
- setIsLoadingProjectFlockKandang(true);
- setSelectedKandang(kandang);
- const ProjectFlockKandangRes = await ProjectFlockApi.customRequest<
- BaseApiResponse,
- 'GET'
- >(getProjectFlockKandangUrl, {
- method: 'GET',
- params: {
- project_flock_id: projectFlockId ?? 0,
- kandang_id: kandang.id,
- },
- });
- if (isResponseSuccess(ProjectFlockKandangRes)) {
- setProjectFlockKandang(ProjectFlockKandangRes);
- setIsLoadingProjectFlockKandang(false);
- if (
- ProjectFlockKandangRes.data.available_quantity &&
- ProjectFlockKandangRes.data.available_quantity > 0
- ) {
- chickinModal.openModal();
- } else {
- alertModal.openModal();
- }
- }
- };
- const handleAfterSubmit = () => {
- chickinModal.closeModal();
- router.push('/production/chickin');
- };
-
- return (
- <>
- {isResponseSuccess(projectFlock) && (
- <>
-
-
-
- data={projectFlock.data?.kandangs}
- columns={[
- {
- header: '#',
- cell: (props) =>
- tableFilterState.pageSize * (tableFilterState.page - 1) +
- props.row.index +
- 1,
- },
- {
- accessorKey: 'name',
- header: 'Nama Kandang',
- },
- {
- header: 'Aksi',
- cell: (props) => {
- return (
- <>
-
- >
- );
- },
- },
- ]}
- page={undefined}
- className={{
- containerClassName: cn({
- 'mb-20':
- isResponseSuccess(projectFlock) &&
- projectFlock.data?.kandangs?.length === 0,
- }),
- tableWrapperClassName: 'overflow-x-auto min-h-full!',
- tableClassName: 'font-inter w-full table-auto min-h-full!',
- headerRowClassName: 'border-b border-b-gray-200',
- headerColumnClassName:
- 'px-6 py-3 text-xs font-semibold text-gray-500 last:flex last:flex-row last:justify-end',
- bodyRowClassName: 'border-b border-b-gray-200',
- bodyColumnClassName:
- 'px-6 py-3 last:flex last:flex-row last:justify-end',
- paginationClassName: 'hidden',
- }}
- />
-
-
-
-
- Chickin Kandang - {selectedKandang?.name}
-
-
-
- {isResponseSuccess(projectFlockKandang) &&
- !isLoadingProjectFlockKandang && (
-
- )}
-
- {
- alertModal.closeModal();
- },
- }}
- />
- >
- )}
- >
- );
-};
-
-export default AddChickin;
diff --git a/src/app/production/chickin/detail/layout.tsx b/src/app/production/chickin/detail/layout.tsx
deleted file mode 100644
index b41c70f9..00000000
--- a/src/app/production/chickin/detail/layout.tsx
+++ /dev/null
@@ -1,11 +0,0 @@
-import SuspenseHelper from "@/components/helper/SuspenseHelper"
-
-const Layout = ({
- children
-}: Readonly<{
- children: React.ReactNode
-}>) => {
- return {children}
-}
-
-export default Layout;
\ No newline at end of file
diff --git a/src/app/production/chickin/detail/page.tsx b/src/app/production/chickin/detail/page.tsx
deleted file mode 100644
index 96647c55..00000000
--- a/src/app/production/chickin/detail/page.tsx
+++ /dev/null
@@ -1,351 +0,0 @@
-'use client';
-
-import Button from '@/components/Button';
-import Card from '@/components/Card';
-import Modal, { useModal } from '@/components/Modal';
-import ConfirmationModal from '@/components/modal/ConfirmationModal';
-import ChickinForm from '@/components/pages/production/chickin/form/ChickinForm';
-import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
-import { ChickinApi } from '@/services/api/production';
-import { BaseApiResponse } from '@/types/api/api-general';
-import {
- Chickin,
- ChickinApprovalPayload,
-} from '@/types/api/production/chickin';
-import { Icon } from '@iconify/react';
-import { useRouter, useSearchParams } from 'next/navigation';
-import { useState } from 'react';
-import toast from 'react-hot-toast';
-import useSWR from 'swr';
-
-/**
- * TODO: Refactor code - pindahin detail ke reuseable component
- * setelah implement approval and reject
- */
-
-const DetailChickin = () => {
- const router = useRouter();
- const searchParams = useSearchParams();
- const chickinId = searchParams.get('chickinId');
- const [isApproveLoading, setIsApproveLoading] = useState(false);
- const [isDeleteLoading, setIsDeleteLoading] = useState(false);
-
- const confirmModal = useModal();
- const deleteModal = useModal();
- const chickinModal = useModal();
- const {
- data: chickin,
- isLoading,
- mutate: refreshChickin,
- } = useSWR(chickinId, (id: number) => ChickinApi.getSingle(id));
-
- const [isApprovedDisabled, setIsApprovedDisabled] = useState(
- // chickin.data?.approval.step_number == 1 ? false : true
- true
- );
- const [isRejectedDisabled, setIsRejectedDisabled] = useState(
- !isApprovedDisabled
- );
- const [approvalAction, setApprovalAction] = useState<'APPROVED' | 'REJECTED'>(
- !isApprovedDisabled ? 'APPROVED' : 'REJECTED'
- );
-
- if (!chickinId) {
- router.back();
-
- return (
-
-
-
- );
- }
-
- if (!isLoading && (!chickin || isResponseError(chickin))) {
- router.replace('/404');
- return;
- }
-
- if (!isResponseSuccess(chickin)) {
- return (
-
-
-
- );
- }
-
- const confirmationModalClickHandler = async ({
- action = 'APPROVED',
- }: {
- action: 'APPROVED' | 'REJECTED';
- }) => {
- if (chickin?.data.id === undefined) return;
- setIsApproveLoading(true);
- const approveChickinRes = await ChickinApi.customRequest<
- BaseApiResponse,
- ChickinApprovalPayload
- >(`/approvals`, {
- method: 'POST',
- payload: {
- action: action,
- approvable_ids: [chickin.data.id],
- },
- });
-
- if (isResponseSuccess(approveChickinRes)) {
- if (refreshChickin) {
- await refreshChickin();
- }
- toast.success(approveChickinRes.message as string);
- }
- if (isResponseError(approveChickinRes)) {
- toast.error(approveChickinRes?.message as string);
- }
- confirmModal.closeModal();
- setIsApproveLoading(false);
- };
-
- const confirmationModalDeleteClickHandler = async () => {
- setIsDeleteLoading(true);
- const deleteProjectFlockRes = await ChickinApi.delete(
- chickin.data?.id as number
- );
-
- if (isResponseSuccess(deleteProjectFlockRes)) {
- toast.success(deleteProjectFlockRes?.message as string);
- router.push('/production/chickin');
- }
- if (isResponseError(deleteProjectFlockRes)) {
- toast.error(deleteProjectFlockRes?.message as string);
- }
- deleteModal.closeModal();
- setIsDeleteLoading(false);
- };
-
- return (
- <>
-
- {isLoading &&
}
- {!isLoading && isResponseSuccess(chickin) && (
- <>
- {/*
-
-
-
*/}
-
-
-
-
Flock
-
- {
- chickin.data.project_flock_kandang?.project_flock.flock
- .name
- }
-
-
-
-
Area
-
- {
- chickin.data.project_flock_kandang?.project_flock.area
- .name
- }
-
-
-
-
Kategori
-
- {chickin.data.project_flock_kandang?.project_flock.category}
-
-
-
-
Lokasi
-
- {
- chickin.data.project_flock_kandang?.project_flock.location
- .name
- }
-
-
-
-
Periode
-
- {chickin.data.project_flock_kandang?.project_flock.period}
-
-
-
-
Kandang
-
- {chickin.data.project_flock_kandang?.kandang.name}
-
-
-
-
-
-
-
-
Flock Kandang
-
- {
- chickin.data.project_flock_kandang?.project_flock.flock
- .name
- }{' '}
- - {chickin.data.project_flock_kandang?.kandang.name}
-
-
-
-
Tanggal Chickin
-
- {chickin.data.chick_in_date
- ? new Date(chickin.data.chick_in_date).toLocaleDateString(
- 'id-ID'
- )
- : '-'}
-
-
-
-
Jumlah (Ekor)
-
- {chickin.data.quantity?.toLocaleString('id-ID')}
-
-
-
-
Catatan
-
{chickin.data.note}
-
-
-
-
-
-
-
- >
- )}
-
-
-
-
-
-
-
- Chickin Kandang -{' '}
- {chickin?.data?.project_flock_kandang &&
- chickin?.data?.project_flock_kandang.kandang?.name}
-
-
-
- {
- refreshChickin();
- chickinModal.closeModal();
- }}
- />
-
-
- {
- confirmationModalClickHandler({
- action: approvalAction,
- });
- },
- }}
- />
- >
- );
-};
-
-export default DetailChickin;
diff --git a/src/app/production/chickin/page.tsx b/src/app/production/chickin/page.tsx
deleted file mode 100644
index ad662f65..00000000
--- a/src/app/production/chickin/page.tsx
+++ /dev/null
@@ -1,10 +0,0 @@
-import ChickinTable from "@/components/pages/production/chickin/ChickinTable";
-
-const Chickin = () => {
- return (
-
- );
-}
-export default Chickin;
\ No newline at end of file
diff --git a/src/app/production/project-flock/add/page.tsx b/src/app/production/project-flock/add/page.tsx
index 60141d80..b323b5f3 100644
--- a/src/app/production/project-flock/add/page.tsx
+++ b/src/app/production/project-flock/add/page.tsx
@@ -1,13 +1,13 @@
-'use client'
+'use client';
-import ProjectFlockForm from "@/components/pages/production/project-flock/form/ProjectFlockForm";
+import ProjectFlockForm from '@/components/pages/production/project-flock/form/ProjectFlockForm';
const AddProjectFlock = () => {
return (
-
-
+
);
-}
+};
-export default AddProjectFlock;
\ No newline at end of file
+export default AddProjectFlock;
diff --git a/src/app/production/project-flock/chickin/add/kandang/layout.tsx b/src/app/production/project-flock/chickin/add/kandang/layout.tsx
new file mode 100644
index 00000000..7220dfa1
--- /dev/null
+++ b/src/app/production/project-flock/chickin/add/kandang/layout.tsx
@@ -0,0 +1,11 @@
+import SuspenseHelper from '@/components/helper/SuspenseHelper';
+
+const Layout = ({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) => {
+ return {children};
+};
+
+export default Layout;
diff --git a/src/app/production/project-flock/chickin/add/kandang/page.tsx b/src/app/production/project-flock/chickin/add/kandang/page.tsx
new file mode 100644
index 00000000..a22039d1
--- /dev/null
+++ b/src/app/production/project-flock/chickin/add/kandang/page.tsx
@@ -0,0 +1,60 @@
+'use client';
+
+import ChickinForm from '@/components/pages/production/chickin/form/ChickinForm';
+import { isResponseSuccess } from '@/lib/api-helper';
+import { ProjectFlockKandangApi } from '@/services/api/production';
+import { useRouter, useSearchParams } from 'next/navigation';
+import useSWR from 'swr';
+
+export default function AddChickinKandang() {
+ const searchParams = useSearchParams();
+ const projectFlockKandangId = searchParams.get('projectFlockKandangId');
+ const projectFlockId = searchParams.get('projectFlockId');
+ const router = useRouter();
+
+ const {
+ data: projectFlockKandang,
+ isLoading: isLoading,
+ mutate: refreshProjectFlockKandang,
+ } = useSWR(
+ `get-single-project-flock-kandang/${projectFlockKandangId}`,
+ async () =>
+ ProjectFlockKandangApi.getSingle(
+ parseInt(projectFlockKandangId as string)
+ )
+ );
+
+ if (!projectFlockKandangId) {
+ router.push(`/production/chickin/add?projectFlockId=${projectFlockId}`);
+ return (
+
+
+
+ );
+ }
+
+ if (!isLoading && !projectFlockKandang) {
+ router.replace('/404');
+ return;
+ }
+
+ const handleAfterSubmit = () => {
+ refreshProjectFlockKandang();
+ };
+
+ return (
+ <>
+
+ {isLoading && }
+ {!isLoading &&
+ isResponseSuccess(projectFlockKandang) &&
+ projectFlockId && (
+
+ )}
+
+ >
+ );
+}
diff --git a/src/app/production/project-flock/chickin/add/layout.tsx b/src/app/production/project-flock/chickin/add/layout.tsx
new file mode 100644
index 00000000..7220dfa1
--- /dev/null
+++ b/src/app/production/project-flock/chickin/add/layout.tsx
@@ -0,0 +1,11 @@
+import SuspenseHelper from '@/components/helper/SuspenseHelper';
+
+const Layout = ({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) => {
+ return {children};
+};
+
+export default Layout;
diff --git a/src/app/production/project-flock/chickin/add/page.tsx b/src/app/production/project-flock/chickin/add/page.tsx
new file mode 100644
index 00000000..bcb4d612
--- /dev/null
+++ b/src/app/production/project-flock/chickin/add/page.tsx
@@ -0,0 +1,20 @@
+'use client';
+
+import { FormHeader } from '@/components/helper/form/FormHeader';
+import ProjectFlockChickinDetail from '@/components/pages/production/project-flock/chickin/ProjectFlockChickinDetail';
+import { useSearchParams } from 'next/navigation';
+
+const AddChickin = () => {
+ const searchParams = useSearchParams();
+ const projectFlockId = searchParams.get('projectFlockId');
+
+ return (
+ <>
+
+ >
+ );
+};
+
+export default AddChickin;
diff --git a/src/app/production/project-flock/chickin/page.tsx b/src/app/production/project-flock/chickin/page.tsx
new file mode 100644
index 00000000..5d105aab
--- /dev/null
+++ b/src/app/production/project-flock/chickin/page.tsx
@@ -0,0 +1,10 @@
+import ChickinTable from '@/components/pages/production/chickin/ChickinTable';
+
+const Chickin = () => {
+ return (
+
+ );
+};
+export default Chickin;
diff --git a/src/app/production/project-flock/detail/edit/page.tsx b/src/app/production/project-flock/detail/edit/page.tsx
index 858d0ca8..f55ce601 100644
--- a/src/app/production/project-flock/detail/edit/page.tsx
+++ b/src/app/production/project-flock/detail/edit/page.tsx
@@ -1,46 +1,51 @@
-'use client'
+'use client';
-
-import ProjectFlockForm from "@/components/pages/production/project-flock/form/ProjectFlockForm";
-import { isResponseError, isResponseSuccess } from "@/lib/api-helper";
-import { ProjectFlockApi } from "@/services/api/production";
-import { useRouter, useSearchParams } from "next/navigation";
-import useSWR from "swr";
+import ProjectFlockForm from '@/components/pages/production/project-flock/form/ProjectFlockForm';
+import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
+import { ProjectFlockApi } from '@/services/api/production/project-flock';
+import { useRouter, useSearchParams } from 'next/navigation';
+import useSWR from 'swr';
const ProjectFlockEdit = () => {
const router = useRouter();
const searchParams = useSearchParams();
- const projectFlockId = searchParams.get("projectFlockId");
+ const projectFlockId = searchParams.get('projectFlockId');
- const { data: projectFlock, isLoading: isLoadingCostumer } = useSWR(
- projectFlockId,
- (id: number) => ProjectFlockApi.getSingle(id)
- );
+ const {
+ data: projectFlock,
+ isLoading: isLoadingProjectFlock,
+ mutate: refreshProjectFlocks,
+ } = useSWR(projectFlockId, (id: number) => ProjectFlockApi.getSingle(id));
- if(!projectFlockId){
+ if (!projectFlockId) {
router.back();
return (
-
-
+
+
);
}
- if(!isLoadingCostumer && (!projectFlock || isResponseError(projectFlock))){
- router.replace("/404");
+ if (
+ !isLoadingProjectFlock &&
+ (!projectFlock || isResponseError(projectFlock))
+ ) {
+ router.replace('/404');
return;
}
return (
-
- {isLoadingCostumer &&
}
- {!isLoadingCostumer && isResponseSuccess(projectFlock) && (
-
+
+ {isLoadingProjectFlock && (
+
+ )}
+ {!isLoadingProjectFlock && isResponseSuccess(projectFlock) && (
+
)}
- )
-}
+ );
+};
-export default ProjectFlockEdit;
\ No newline at end of file
+export default ProjectFlockEdit;
diff --git a/src/app/production/project-flock/detail/layout.tsx b/src/app/production/project-flock/detail/layout.tsx
index b41c70f9..7220dfa1 100644
--- a/src/app/production/project-flock/detail/layout.tsx
+++ b/src/app/production/project-flock/detail/layout.tsx
@@ -1,11 +1,11 @@
-import SuspenseHelper from "@/components/helper/SuspenseHelper"
+import SuspenseHelper from '@/components/helper/SuspenseHelper';
const Layout = ({
- children
+ children,
}: Readonly<{
- children: React.ReactNode
+ children: React.ReactNode;
}>) => {
- return
{children}
-}
+ return
{children};
+};
-export default Layout;
\ No newline at end of file
+export default Layout;
diff --git a/src/app/production/project-flock/detail/page.tsx b/src/app/production/project-flock/detail/page.tsx
index bea96b84..91d4dfd5 100644
--- a/src/app/production/project-flock/detail/page.tsx
+++ b/src/app/production/project-flock/detail/page.tsx
@@ -2,7 +2,7 @@
import ProjectFlockForm from '@/components/pages/production/project-flock/form/ProjectFlockForm';
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
-import { ProjectFlockApi } from '@/services/api/production';
+import { ProjectFlockApi } from '@/services/api/production/project-flock';
import { useRouter, useSearchParams } from 'next/navigation';
import useSWR from 'swr';
@@ -37,12 +37,16 @@ const ProjectFlockDetail = () => {
}
return (
-
+
{isLoadingProjectFlock && (
)}
- {!isLoadingProjectFlock && isResponseSuccess(projectFlock) && (
-
+ {isResponseSuccess(projectFlock) && (
+
)}
);
diff --git a/src/app/production/project-flock/page.tsx b/src/app/production/project-flock/page.tsx
index d264d9e4..79feb41f 100644
--- a/src/app/production/project-flock/page.tsx
+++ b/src/app/production/project-flock/page.tsx
@@ -1,11 +1,11 @@
-import ProjectFlockTable from "@/components/pages/production/project-flock/ProjectFlockTable";
+import ProjectFlockTable from '@/components/pages/production/project-flock/ProjectFlockTable';
const ProjectFlock = () => {
return (
-
-
+
);
-}
+};
export default ProjectFlock;
diff --git a/src/app/production/recording/add/layout.tsx b/src/app/production/recording/add/layout.tsx
new file mode 100644
index 00000000..7220dfa1
--- /dev/null
+++ b/src/app/production/recording/add/layout.tsx
@@ -0,0 +1,11 @@
+import SuspenseHelper from '@/components/helper/SuspenseHelper';
+
+const Layout = ({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) => {
+ return {children};
+};
+
+export default Layout;
diff --git a/src/app/production/recording/grading/add/page.tsx b/src/app/production/recording/grading/add/page.tsx
new file mode 100644
index 00000000..9b918d98
--- /dev/null
+++ b/src/app/production/recording/grading/add/page.tsx
@@ -0,0 +1,49 @@
+'use client';
+
+import { useRouter, useSearchParams } from 'next/navigation';
+import useSWR from 'swr';
+import GradingForm from '@/components/pages/production/recording/grading/form/GradingForm';
+import { RecordingApi } from '@/services/api/production';
+import { isResponseSuccess } from '@/lib/api-helper';
+
+const AddGrading = () => {
+ const router = useRouter();
+ const searchParams = useSearchParams();
+
+ const recordingId = searchParams.get('recording_id');
+
+ const { data: recording, isLoading: isLoadingRecording } = useSWR(
+ recordingId && recordingId !== 'new' ? [recordingId] : null,
+ ([id]) => RecordingApi.getSingle(parseInt(id))
+ );
+
+ if (
+ recordingId &&
+ recordingId !== 'new' &&
+ !isLoadingRecording &&
+ (!recording || !isResponseSuccess(recording))
+ ) {
+ router.replace('/404');
+ return;
+ }
+
+ return (
+
+ {recordingId && recordingId !== 'new' && isLoadingRecording && (
+
+ )}
+ {(!recordingId ||
+ recordingId === 'new' ||
+ (!isLoadingRecording && recording && isResponseSuccess(recording))) && (
+
+ )}
+
+ );
+};
+
+export default AddGrading;
diff --git a/src/app/production/recording/grading/detail/edit/page.tsx b/src/app/production/recording/grading/detail/edit/page.tsx
new file mode 100644
index 00000000..0a65f528
--- /dev/null
+++ b/src/app/production/recording/grading/detail/edit/page.tsx
@@ -0,0 +1,53 @@
+'use client';
+
+import { useRouter, useSearchParams } from 'next/navigation';
+import useSWR from 'swr';
+import GradingForm from '@/components/pages/production/recording/grading/form/GradingForm';
+import { RecordingApi } from '@/services/api/production';
+import { isResponseSuccess } from '@/lib/api-helper';
+
+const EditGrading = () => {
+ const router = useRouter();
+ const searchParams = useSearchParams();
+
+ const recordingId = searchParams.get('recordingId');
+ const gradingId = searchParams.get('gradingId');
+
+ const { data: recording, isLoading: isLoadingRecording } = useSWR(
+ recordingId ? [recordingId] : null,
+ ([id]) => RecordingApi.getSingle(parseInt(id))
+ );
+
+ if (!recordingId) {
+ router.back();
+
+ return (
+
+
+
+ );
+ }
+
+ if (!isLoadingRecording && (!recording || !isResponseSuccess(recording))) {
+ router.replace('/404');
+ return;
+ }
+
+ return (
+
+ {isLoadingRecording && (
+
+ )}
+ {!isLoadingRecording && recording && isResponseSuccess(recording) && (
+ egg.id === parseInt(gradingId || '0')
+ )}
+ />
+ )}
+
+ );
+};
+
+export default EditGrading;
diff --git a/src/app/production/recording/grading/detail/page.tsx b/src/app/production/recording/grading/detail/page.tsx
new file mode 100644
index 00000000..6a5fbcba
--- /dev/null
+++ b/src/app/production/recording/grading/detail/page.tsx
@@ -0,0 +1,52 @@
+'use client';
+
+import { useRouter, useSearchParams } from 'next/navigation';
+import useSWR from 'swr';
+import GradingForm from '@/components/pages/production/recording/grading/form/GradingForm';
+import { RecordingApi } from '@/services/api/production';
+import { isResponseSuccess } from '@/lib/api-helper';
+
+const DetailGrading = () => {
+ const router = useRouter();
+ const searchParams = useSearchParams();
+
+ const gradingId = searchParams.get('gradingId');
+
+ const { data: grading, isLoading: isLoadingGrading } = useSWR(
+ gradingId ? [gradingId] : null,
+ ([id]) => RecordingApi.getSingle(parseInt(id))
+ );
+
+ if (!gradingId) {
+ router.back();
+
+ return (
+
+
+
+ );
+ }
+
+ if (!isLoadingGrading && (!grading || !isResponseSuccess(grading))) {
+ router.replace('/404');
+ return;
+ }
+
+ return (
+
+ {isLoadingGrading && (
+
+ )}
+ {!isLoadingGrading && grading && isResponseSuccess(grading) && (
+ egg.id === parseInt(gradingId)
+ )}
+ />
+ )}
+
+ );
+};
+
+export default DetailGrading;
diff --git a/src/app/production/recording/grading/layout.tsx b/src/app/production/recording/grading/layout.tsx
new file mode 100644
index 00000000..7220dfa1
--- /dev/null
+++ b/src/app/production/recording/grading/layout.tsx
@@ -0,0 +1,11 @@
+import SuspenseHelper from '@/components/helper/SuspenseHelper';
+
+const Layout = ({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) => {
+ return {children};
+};
+
+export default Layout;
diff --git a/src/app/production/transfer-to-laying/detail/edit/page.tsx b/src/app/production/transfer-to-laying/detail/edit/page.tsx
index 9003dbba..d5498e08 100644
--- a/src/app/production/transfer-to-laying/detail/edit/page.tsx
+++ b/src/app/production/transfer-to-laying/detail/edit/page.tsx
@@ -8,91 +8,6 @@ import TransferToLayingForm from '@/components/pages/production/transfer-to-layi
import { TransferToLayingApi } from '@/services/api/production/transfer-to-laying';
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
-import { TransferToLaying } from '@/types/api/production/transfer-to-laying';
-
-// TODO: delete dummy data
-const DUMMY_TRANSFER_TO_LAYING_EDIT: TransferToLaying = {
- id: 1,
- transfer_date: '2025-10-14',
- flock_source: {
- id: 1,
- name: 'Flock asal test',
- },
- flock_destination: {
- id: 2,
- name: 'Flock tujuan destination',
- },
- quantity: 10,
- kandangs: [
- {
- kandang: {
- id: 1,
- name: 'Kandang test',
- status: 'ACTIVE',
- location: {
- id: 1,
- name: 'test location',
- address: 'test address 1',
- area: { id: 1, name: 'test area 1' },
- },
- pic: {
- id: 1,
- id_user: 2,
- email: 'test@gmail.com',
- name: 'test',
- },
- created_user: {
- id: 1,
- id_user: 2,
- email: 'test@gmail.com',
- name: 'test',
- },
- created_at: '14-10-2025',
- updated_at: '14-10-2025',
- },
- quantity: 8,
- },
- {
- kandang: {
- id: 1,
- name: 'Kandang test 2',
- status: 'ACTIVE',
- location: {
- id: 1,
- name: 'test location',
- address: 'test address 1',
- area: { id: 1, name: 'test area 1' },
- },
- pic: {
- id: 1,
- id_user: 2,
- email: 'test@gmail.com',
- name: 'test',
- },
- created_user: {
- id: 1,
- id_user: 2,
- email: 'test@gmail.com',
- name: 'test',
- },
- created_at: '14-10-2025',
- updated_at: '14-10-2025',
- },
- quantity: 2,
- },
- ],
- reason: 'Test alasan',
-
- created_user: {
- id: 1,
- id_user: 2,
- email: 'test@gmail.com',
- name: 'test',
- },
- created_at: '14-10-2025',
- updated_at: '14-10-2025',
-};
-
const TransferToLayingEdit = () => {
const router = useRouter();
const searchParams = useSearchParams();
@@ -114,33 +29,33 @@ const TransferToLayingEdit = () => {
);
}
- // TODO: remove dummy data and integrate with real API
if (
!isLoadingTransferToLaying &&
- (!transferToLaying ||
- (isResponseError(transferToLaying) && !DUMMY_TRANSFER_TO_LAYING_EDIT))
+ (!transferToLaying || isResponseError(transferToLaying))
) {
router.replace('/404');
return;
}
+ if (
+ isResponseSuccess(transferToLaying) &&
+ transferToLaying.data.approval.step_number === 2
+ ) {
+ router.replace('/production/transfer-to-laying');
+ return;
+ }
+
return (
{isLoadingTransferToLaying && (
)}
- {/* {!isLoadingTransferToLaying && isResponseSuccess(transferToLaying) && (
+ {!isLoadingTransferToLaying && isResponseSuccess(transferToLaying) && (
- )} */}
-
- {/* TODO: remove this dummy data and integrate to real API */}
-
+ )}
);
};
diff --git a/src/app/production/transfer-to-laying/detail/page.tsx b/src/app/production/transfer-to-laying/detail/page.tsx
index de5426c8..9ff6ed5e 100644
--- a/src/app/production/transfer-to-laying/detail/page.tsx
+++ b/src/app/production/transfer-to-laying/detail/page.tsx
@@ -8,91 +8,6 @@ import TransferToLayingForm from '@/components/pages/production/transfer-to-layi
import { TransferToLayingApi } from '@/services/api/production/transfer-to-laying';
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
-import { TransferToLaying } from '@/types/api/production/transfer-to-laying';
-
-// TODO: delete dummy data
-const DUMMY_TRANSFER_TO_LAYING_DETAIL: TransferToLaying = {
- id: 1,
- transfer_date: '2025-10-14',
- flock_source: {
- id: 1,
- name: 'Flock asal test',
- },
- flock_destination: {
- id: 2,
- name: 'Flock tujuan destination',
- },
- quantity: 10,
- kandangs: [
- {
- kandang: {
- id: 1,
- name: 'Kandang test',
- status: 'ACTIVE',
- location: {
- id: 1,
- name: 'test location',
- address: 'test address 1',
- area: { id: 1, name: 'test area 1' },
- },
- pic: {
- id: 1,
- id_user: 2,
- email: 'test@gmail.com',
- name: 'test',
- },
- created_user: {
- id: 1,
- id_user: 2,
- email: 'test@gmail.com',
- name: 'test',
- },
- created_at: '14-10-2025',
- updated_at: '14-10-2025',
- },
- quantity: 8,
- },
- {
- kandang: {
- id: 1,
- name: 'Kandang test 2',
- status: 'ACTIVE',
- location: {
- id: 1,
- name: 'test location',
- address: 'test address 1',
- area: { id: 1, name: 'test area 1' },
- },
- pic: {
- id: 1,
- id_user: 2,
- email: 'test@gmail.com',
- name: 'test',
- },
- created_user: {
- id: 1,
- id_user: 2,
- email: 'test@gmail.com',
- name: 'test',
- },
- created_at: '14-10-2025',
- updated_at: '14-10-2025',
- },
- quantity: 2,
- },
- ],
- reason: 'Test alasan',
-
- created_user: {
- id: 1,
- id_user: 2,
- email: 'test@gmail.com',
- name: 'test',
- },
- created_at: '14-10-2025',
- updated_at: '14-10-2025',
-};
-
const TransferToLayingDetail = () => {
const router = useRouter();
const searchParams = useSearchParams();
@@ -114,11 +29,9 @@ const TransferToLayingDetail = () => {
);
}
- // TODO: remove dummy data and integrate with real API
if (
!isLoadingTransferToLaying &&
- (!transferToLaying ||
- (isResponseError(transferToLaying) && !DUMMY_TRANSFER_TO_LAYING_DETAIL))
+ (!transferToLaying || isResponseError(transferToLaying))
) {
router.replace('/404');
return;
@@ -129,18 +42,13 @@ const TransferToLayingDetail = () => {
{isLoadingTransferToLaying && (
)}
- {/* {!isLoadingTransferToLaying && isResponseSuccess(transferToLaying) && (
+
+ {!isLoadingTransferToLaying && isResponseSuccess(transferToLaying) && (
- )} */}
-
- {/* TODO: remove this dummy data and integrate to real API */}
-
+ )}
);
};
diff --git a/src/components/Button.tsx b/src/components/Button.tsx
index 7cad5b58..2f209ece 100644
--- a/src/components/Button.tsx
+++ b/src/components/Button.tsx
@@ -3,7 +3,7 @@ import Link from 'next/link';
import { cn } from '@/lib/helper';
import { Color } from '@/types/theme';
-interface ButtonProps extends react.ComponentProps<'button'> {
+export interface ButtonProps extends react.ComponentProps<'button'> {
variant?: 'soft' | 'outline' | 'dash' | 'ghost' | 'link' | 'active';
color?: Color;
href?: string;
diff --git a/src/components/Card.tsx b/src/components/Card.tsx
index 06438390..7b022971 100644
--- a/src/components/Card.tsx
+++ b/src/components/Card.tsx
@@ -1,13 +1,12 @@
'use client';
-import {
- HTMLAttributes,
- ReactNode,
-} from 'react';
+import { HTMLAttributes, ReactNode } from 'react';
import { cn } from '@/lib/helper';
+import Image from 'next/image';
-export interface CardProps extends Omit
, 'className'> {
+export interface CardProps
+ extends Omit, 'className'> {
title?: string;
subtitle?: string;
image?: string;
@@ -44,17 +43,17 @@ const Card = ({
const baseClasses = 'card bg-base-100';
const variantClasses = {
- 'default': '',
- 'compact': 'card-compact',
- 'bordered': 'border border-base-300',
- 'shadow': 'shadow-xl',
+ default: '',
+ compact: 'card-compact',
+ bordered: 'border border-base-300',
+ shadow: 'shadow-xl',
'image-full': 'card-side card-compact shadow-xl',
};
const sizeClasses = {
- 'sm': 'w-64',
- 'md': 'w-96',
- 'lg': 'w-[28rem]',
+ sm: 'w-64',
+ md: 'w-96',
+ lg: 'w-[28rem]',
};
return cn(
@@ -84,9 +83,9 @@ const Card = ({
const getTitleClasses = () => {
const sizeClasses = {
- 'sm': 'text-lg',
- 'md': 'text-xl',
- 'lg': 'text-2xl',
+ sm: 'text-lg',
+ md: 'text-xl',
+ lg: 'text-2xl',
};
return cn('card-title font-bold', sizeClasses[size], className?.title);
@@ -108,7 +107,7 @@ const Card = ({
return (
-
{image && (
-
{
@@ -8,31 +15,34 @@ export const useModal = () => {
const [open, setOpen] = useState(false);
const openModal = useCallback(() => {
+ if (!ref.current) return;
+ ref.current.show();
setOpen(true);
-
- ref.current?.showModal();
}, []);
const closeModal = useCallback(() => {
+ if (!ref.current) return;
+ ref.current.close();
setOpen(false);
- ref.current?.close();
}, []);
const toggle = useCallback(() => {
- if (open) {
- closeModal();
- } else {
- openModal();
- }
+ open ? closeModal() : openModal();
}, [open, closeModal, openModal]);
- if (ref.current) {
- ref.current.addEventListener('close', () => {
- closeModal();
- });
- }
+ useEffect(() => {
+ const dialog = ref.current;
+ if (!dialog) return;
- return { ref, open, setOpen, openModal, closeModal, toggle } as const;
+ const handleClose = () => setOpen(false);
+ dialog.addEventListener('close', handleClose);
+
+ return () => {
+ dialog.removeEventListener('close', handleClose);
+ };
+ }, []);
+
+ return { ref, open, openModal, closeModal, toggle } as const;
};
interface ModalProps {
@@ -46,15 +56,19 @@ interface ModalProps {
}
const Modal = ({ ref, children, closeOnBackdrop, className }: ModalProps) => {
- return (
-