feat(FE): Add constants transformation utilities and API service

This commit is contained in:
rstubryan
2026-03-02 09:57:21 +07:00
parent 04b5a7bd4d
commit aa1ef7a559
3 changed files with 237 additions and 0 deletions
+126
View File
@@ -3,6 +3,12 @@ import 'moment/locale/id';
import { twMerge } from 'tailwind-merge';
import clsx, { ClassValue } from 'clsx';
import { SidebarMenuItem } from '@/components/molecules/SidebarMenu';
import { OptionType } from '@/components/input/SelectInput';
import {
ConstantsApiResponse,
ProductFlagMappingUI,
TransformedConstants,
} from '@/types/api/constants/constants';
// set locale globally
moment.locale('id');
@@ -179,3 +185,123 @@ export function findMenuPath(
return null;
}
/**
* Transform a string value to OptionType with formatted label
* Example: "AYAM-AFKIR" -> { label: "Ayam Afkir", value: "AYAM-AFKIR" }
*/
export function toOption(value: string): OptionType {
return {
value,
label: formatConstantLabel(value),
};
}
/**
* Format constant label by:
* 1. Replacing underscores/hyphens with spaces
* 2. Converting to title case
* 3. Handling special cases
*/
export function formatConstantLabel(value: string): string {
const specialCases: Record<string, string> = {
'PRE-STARTER': 'Pre Starter',
BOP: 'BOP',
SAPRONAK: 'SAPRONAK',
OVK: 'OVK',
DOC: 'DOC',
};
if (specialCases[value]) {
return specialCases[value];
}
const withSpaces = value.replace(/[-_]/g, ' ');
return formatTitleCase(withSpaces);
}
/**
* Transform product_flag_mapping from API format to UI format
*/
export function transformProductFlagMapping(
mapping: ConstantsApiResponse['product_flag_mapping']
): ProductFlagMappingUI {
return {
flags: mapping.flags.map(toOption),
options: mapping.options.map((opt) => ({
flag: toOption(opt.flag),
sub_flags: opt.sub_flags.map(toOption),
allow_without_sub_flag: opt.allow_without_sub_flag,
})),
sub_flag_to_flag: mapping.sub_flag_to_flag,
};
}
/**
* Transform approval workflows from API format to UI format
*/
export function transformApprovalWorkflows(
workflows: ConstantsApiResponse['approval_workflows']
) {
return workflows.map((workflow) => ({
key: workflow.key,
steps: workflow.steps.map((step) => ({
value: String(step.step_number),
label: step.step_name,
})),
}));
}
/**
* Transform adjustment transaction subtypes from API format to UI format
*/
export function transformAdjustmentSubtypes(
subtypes: ConstantsApiResponse['adjustment']['transaction_subtypes']
) {
return {
RECORDING: subtypes.RECORDING.map(toOption),
PENJUALAN: subtypes.PENJUALAN.map(toOption),
PEMBELIAN: subtypes.PEMBELIAN.map(toOption),
};
}
/**
* Transform legacy flag aliases from API format to UI format
*/
export function transformLegacyFlagAliases(
aliases: ConstantsApiResponse['legacy_flag_aliases']
): OptionType[] {
return Object.entries(aliases).map(([key, value]) => ({
value: key,
label: formatConstantLabel(key),
}));
}
/**
* Transform the entire constants API response to UI format
*/
export function transformConstants(
data: ConstantsApiResponse
): TransformedConstants {
return {
warehouse_types: data.warehouse_types.map(toOption),
supplier_categories: data.supplier_categories.map(toOption),
customer_supplier_types: data.customer_supplier_types.map(toOption),
adjustment: {
transaction_subtypes: transformAdjustmentSubtypes(
data.adjustment.transaction_subtypes
),
},
approval_workflows: transformApprovalWorkflows(data.approval_workflows),
flags: data.flags.map(toOption),
product_flag_mapping: transformProductFlagMapping(
data.product_flag_mapping
),
legacy_flag_aliases: transformLegacyFlagAliases(data.legacy_flag_aliases),
stock_log: {
log_types: data.stock_log.log_types.map(toOption),
transaction_types: data.stock_log.transaction_types.map(toOption),
},
};
}
+25
View File
@@ -0,0 +1,25 @@
import { httpClient } from '@/services/http/client';
import {
ConstantsApiResponse,
TransformedConstants,
} from '@/types/api/constants/constants';
import { transformConstants } from '@/lib/helper';
class ConstantsApiService {
async fetchConstants(): Promise<ConstantsApiResponse | undefined> {
try {
const response = await httpClient<ConstantsApiResponse>('/constants');
return response;
} catch {
return undefined;
}
}
async fetchTransformedConstants(): Promise<TransformedConstants | undefined> {
const data = await this.fetchConstants();
if (!data) return undefined;
return transformConstants(data);
}
}
export const ConstantsApi = new ConstantsApiService();
+86
View File
@@ -0,0 +1,86 @@
import { OptionType } from '@/components/input/SelectInput';
export type ApprovalWorkflowStep = {
step_number: number;
step_name: string;
};
export type ApprovalWorkflow = {
key: string;
steps: ApprovalWorkflowStep[];
};
export type ProductFlagMappingOption = {
flag: string;
sub_flags: string[];
allow_without_sub_flag: boolean;
};
export type ProductFlagMappingApiResponse = {
flags: string[];
options: ProductFlagMappingOption[];
sub_flag_to_flag: Record<string, string>;
};
export type AdjustmentTransactionSubtypes = {
RECORDING: string[];
PENJUALAN: string[];
PEMBELIAN: string[];
};
export type StockLogConfig = {
log_types: string[];
transaction_types: string[];
};
export type ConstantsApiResponse = {
warehouse_types: string[];
supplier_categories: string[];
customer_supplier_types: string[];
adjustment: {
transaction_subtypes: AdjustmentTransactionSubtypes;
};
approval_workflows: ApprovalWorkflow[];
flags: string[];
product_flag_mapping: ProductFlagMappingApiResponse;
legacy_flag_aliases: Record<string, string>;
stock_log: StockLogConfig;
};
export type ProductFlagMappingOptionUI = {
flag: OptionType;
sub_flags: OptionType[];
allow_without_sub_flag: boolean;
};
export type ProductFlagMappingUI = {
flags: OptionType[];
options: ProductFlagMappingOptionUI[];
sub_flag_to_flag: Record<string, string>;
};
export type ApprovalWorkflowUI = {
key: string;
steps: OptionType[];
};
export type TransformedConstants = {
warehouse_types: OptionType[];
supplier_categories: OptionType[];
customer_supplier_types: OptionType[];
adjustment: {
transaction_subtypes: {
RECORDING: OptionType[];
PENJUALAN: OptionType[];
PEMBELIAN: OptionType[];
};
};
approval_workflows: ApprovalWorkflowUI[];
flags: OptionType[];
product_flag_mapping: ProductFlagMappingUI;
legacy_flag_aliases: OptionType[];
stock_log: {
log_types: OptionType[];
transaction_types: OptionType[];
};
};