From 88fe135cb40a7d0db9a4d5b35319d5bf9fc17309 Mon Sep 17 00:00:00 2001 From: Adnan Zahir Date: Fri, 3 Oct 2025 21:58:55 +0700 Subject: [PATCH 01/10] chore(CI): added gitlab ci yaml file for notify MR and MR-merged events --- .gitlab-ci.yml | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 .gitlab-ci.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 00000000..efda72f0 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,76 @@ +stages: [notify] + +# --- Notify when MR is opened/updated --- +notify_discord_mr: + stage: notify + image: alpine:3.20 + rules: + - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' + 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}" + + 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" + +# --- Notify when MR is merged --- +notify_discord_merge: + stage: notify + image: alpine:3.20 + rules: + # Only run for merge request pipelines that are in merged state + - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_STATE == "merged"' + 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}" + + 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" From 21b93963230ae90e5a8b489e465951152aead512 Mon Sep 17 00:00:00 2001 From: sweetpotet Date: Wed, 8 Oct 2025 16:40:30 +0700 Subject: [PATCH 02/10] feat(FE-33): create customers forms --- src/app/master-data/customer/add/page.tsx | 11 + .../master-data/customer/detail/edit/page.tsx | 0 src/app/master-data/customer/detail/page.tsx | 0 src/app/master-data/customer/page.tsx | 11 + src/components/helper/RequireAuth.tsx | 160 +++++++- src/components/input/TextArea.tsx | 124 ++++++ .../master-data/customer/CustomersTable.tsx | 245 ++++++++++++ .../customer/form/CustomerForm.schema.ts | 40 ++ .../customer/form/CustomerForm.tsx | 377 ++++++++++++++++++ src/config/constant.ts | 11 + src/services/api/master-data.ts | 11 + src/types/api/master-data/customer.d.ts | 27 ++ 12 files changed, 1008 insertions(+), 9 deletions(-) create mode 100644 src/app/master-data/customer/add/page.tsx create mode 100644 src/app/master-data/customer/detail/edit/page.tsx create mode 100644 src/app/master-data/customer/detail/page.tsx create mode 100644 src/app/master-data/customer/page.tsx create mode 100644 src/components/input/TextArea.tsx create mode 100644 src/components/pages/master-data/customer/CustomersTable.tsx create mode 100644 src/components/pages/master-data/customer/form/CustomerForm.schema.ts create mode 100644 src/components/pages/master-data/customer/form/CustomerForm.tsx create mode 100644 src/types/api/master-data/customer.d.ts diff --git a/src/app/master-data/customer/add/page.tsx b/src/app/master-data/customer/add/page.tsx new file mode 100644 index 00000000..274b7d90 --- /dev/null +++ b/src/app/master-data/customer/add/page.tsx @@ -0,0 +1,11 @@ +import CustomerForm from "@/components/pages/master-data/customer/form/CustomerForm"; + +const AddNonstock = () => { + return ( +
+ +
+ ); +} + +export default AddNonstock; \ No newline at end of file diff --git a/src/app/master-data/customer/detail/edit/page.tsx b/src/app/master-data/customer/detail/edit/page.tsx new file mode 100644 index 00000000..e69de29b diff --git a/src/app/master-data/customer/detail/page.tsx b/src/app/master-data/customer/detail/page.tsx new file mode 100644 index 00000000..e69de29b diff --git a/src/app/master-data/customer/page.tsx b/src/app/master-data/customer/page.tsx new file mode 100644 index 00000000..56281702 --- /dev/null +++ b/src/app/master-data/customer/page.tsx @@ -0,0 +1,11 @@ +import CustomersTable from "@/components/pages/master-data/customer/CustomersTable"; + +const Nonstock = () => { + return ( +
+ +
+ ) +}; + +export default Nonstock; \ No newline at end of file diff --git a/src/components/helper/RequireAuth.tsx b/src/components/helper/RequireAuth.tsx index 9bc199f9..1d9d86b4 100644 --- a/src/components/helper/RequireAuth.tsx +++ b/src/components/helper/RequireAuth.tsx @@ -9,6 +9,145 @@ import { httpClientFetcher, SWRHttpKey } from '@/services/http/client'; import { isResponseSuccess } from '@/lib/api-helper'; import { GetMeResponse } from '@/types/api/api-general'; +// TODO: delete this later, DONT HARDCODE USER DATA +const DUMMY_USER = { + id: 1, + email: 'admin@mbugroup.id', + npk: '0001', + name: 'Super Admin', + image: null, + created_at: '2025-09-30T03:24:20.899229Z', + updated_at: '2025-09-30T03:24:20.899229Z', + roles: [ + { + id: 1, + key: 'mbu.super_admin', + name: 'MBU Administrator', + client: { + id: 1, + name: 'PT Mitra Berlian Unggas', + alias: 'MBU', + }, + permissions: [ + { + id: 1, + name: 'mbu:purchase:read', + action: 'read', + client: { + id: 1, + name: 'PT Mitra Berlian Unggas', + alias: 'MBU', + }, + }, + { + id: 2, + name: 'mbu:purchase:create', + action: 'create', + client: { + id: 1, + name: 'PT Mitra Berlian Unggas', + alias: 'MBU', + }, + }, + { + id: 3, + name: 'mbu:purchase:approve', + action: 'approve', + client: { + id: 1, + name: 'PT Mitra Berlian Unggas', + alias: 'MBU', + }, + }, + ], + }, + { + id: 2, + key: 'lti.super_admin', + name: 'LTI Administrator', + client: { + id: 2, + name: 'PT Lumbung Telur Indonesia', + alias: 'LTI', + }, + permissions: [ + { + id: 4, + name: 'lti:purchase:read', + action: 'read', + client: { + id: 2, + name: 'PT Lumbung Telur Indonesia', + alias: 'LTI', + }, + }, + { + id: 5, + name: 'lti:purchase:create', + action: 'create', + client: { + id: 2, + name: 'PT Lumbung Telur Indonesia', + alias: 'LTI', + }, + }, + { + id: 6, + name: 'lti:purchase:approve', + action: 'approve', + client: { + id: 2, + name: 'PT Lumbung Telur Indonesia', + alias: 'LTI', + }, + }, + ], + }, + { + id: 3, + key: 'manbu.super_admin', + name: 'MANBU Administrator', + client: { + id: 3, + name: 'PT Mandiri Berlian Unggas', + alias: 'MANBU', + }, + permissions: [ + { + id: 7, + name: 'manbu:purchase:read', + action: 'read', + client: { + id: 3, + name: 'PT Mandiri Berlian Unggas', + alias: 'MANBU', + }, + }, + { + id: 8, + name: 'manbu:purchase:create', + action: 'create', + client: { + id: 3, + name: 'PT Mandiri Berlian Unggas', + alias: 'MANBU', + }, + }, + { + id: 9, + name: 'manbu:purchase:approve', + action: 'approve', + client: { + id: 3, + name: 'PT Mandiri Berlian Unggas', + alias: 'MANBU', + }, + }, + ], + }, + ], +}; + interface RequireAuthProps { children?: ReactNode; } @@ -37,19 +176,22 @@ const RequireAuth = ({ children }: RequireAuthProps) => { if (isResponseSuccess(userResponse)) { setUser(userResponse.data); } else { - router.replace(process.env.NEXT_PUBLIC_SSO_LOGIN_URL as string); + // router.replace(process.env.NEXT_PUBLIC_SSO_LOGIN_URL as string); + // TODO: remove this later, DONT HARDCODE USER DATA + setUser(DUMMY_USER); } }, [userResponse, setIsLoadingUser, setUser]); - if (isLoadingUserResponse && !userResponse) { - return ( -
- -
- ); - } + // TODO: uncomment this later + // if (isLoadingUserResponse && !userResponse) { + // return ( + //
+ // + //
+ // ); + // } return <>{children}; }; -export default RequireAuth; +export default RequireAuth; \ No newline at end of file diff --git a/src/components/input/TextArea.tsx b/src/components/input/TextArea.tsx new file mode 100644 index 00000000..b4a6c9f5 --- /dev/null +++ b/src/components/input/TextArea.tsx @@ -0,0 +1,124 @@ +'use client'; + +import { + ChangeEventHandler, + FocusEventHandler, + ReactNode, +} from 'react'; + +import { cn } from '@/lib/helper'; + +export interface TextAreaProps { + label?: string; + bottomLabel?: string; + name: string; + value?: string | number; + placeholder?: string; + className?: { + wrapper?: string; + label?: string; + inputWrapper?: string; + input?: string; + }; + isError?: boolean; + isValid?: boolean; + disabled?: boolean; + readOnly?: boolean; + required?: boolean; + isLoading?: boolean; + errorMessage?: string; + startAdornment?: ReactNode; + endAdornment?: ReactNode; + onChange?: ChangeEventHandler; + onBlur?: FocusEventHandler; + cols?: number; +} + +const TextArea = ({ + label, + bottomLabel, + name, + value, + placeholder, + className, + isError, + isValid, + errorMessage, + startAdornment, + endAdornment, + disabled = false, + required = false, + onChange, + onBlur, + readOnly = false, + isLoading = false, + cols = 3 +}: TextAreaProps) => { + return ( +
+ {label && ( + + )} + {startAdornment && startAdornment} + +