import type { FieldsInfo, FieldsJoinMapping, JoinTable, WhereCondition } from '@/types/model'; import { client, isType, type FieldType, type App, type Layout, type OneOf, type Properties, } from './kintone-rest-api-client'; import type { ComboboxItem, DropdownItem } from 'kintone-ui-component'; import { isSpecialType, type SpecialType } from './join'; export const EMPTY_OPTION = { value: '', label: '--------', } as DropdownItem; export function generateId(): string { const timestamp = new Date().getTime().toString(36); const randomNum = Math.random().toString(36).substring(2, 11); return `${timestamp}-${randomNum}`; } export function search(list: Array, id: string) { if (!list) return; return list.find((item) => item.id === id); } export const getEmptyWhereCondition = () => ({ field: '', condition: '', data: '', id: generateId() }) as WhereCondition; export const getEmptyOnCondition = () => ({ leftField: '', rightField: '', id: generateId() }) as FieldsJoinMapping; export const getEmptyFieldsMapping = () => ({ leftField: '', rightField: '', id: generateId() }) as FieldsJoinMapping; export function createEmptyJoinTable(id = generateId()) { return resetTable({ id, app: '' } as JoinTable); } export function resetTable(table: JoinTable) { table.table = ''; return resetConditions(table); } export function resetConditions(table: JoinTable) { table.onConditions = [getEmptyOnCondition()]; table.fieldsMapping = [getEmptyFieldsMapping()]; table.whereConditions = [getEmptyWhereCondition()]; return table; } const LIMIT = 100; // 毎回請求の最大値 export const loadApps = async (offset = 0, _apps: DropdownItem[] = []): Promise => { const { apps } = await client.app.getApps({ limit: LIMIT, offset }); const allApps: DropdownItem[] = [ ..._apps, ...apps.map((app: App) => ({ value: app.appId, label: app.name + '(ID: ' + app.appId + ')' })), ]; if (apps.length === LIMIT) { return loadApps(offset + LIMIT, allApps); } allApps.sort((a, b) => Number(b.value) - Number(a.value)); allApps.unshift(EMPTY_OPTION); return allApps; }; export const loadAppFieldsAndLayout = async (appId: string | number = kintone.app.getId() as number) => { const fields = (await client.app.getFormFields({ app: appId })).properties; return { fields: flatFields(fields), layout: (await client.app.getFormLayout({ app: appId })).layout, } as FieldsInfo; }; function flatFields(fields: Properties) { const subtableFields = {} as Properties; Object.values(fields).forEach((field) => { if (isType.SUBTABLE(field)) { Object.values(field.fields).forEach((subField) => { const copy = JSON.parse(JSON.stringify(subField)) as typeof subField & {originLabel:string, tableCode:string}; copy.label = '[' + field.label + '].' + subField.label; copy.originLabel = subField.label; copy.tableCode = field.code; subtableFields[subField.code] = copy; }); } }); return { ...fields, ...subtableFields }; } type FilterType = Array; type Param = { subTableCode: string | undefined; filterType?: FilterType; baseFilter: FieldType[] | undefined; dependFilterField?: OneOf; defaultLabel?: string; defaultDisableCallback?: (field: OneOf) => boolean; needAllSubTableField?: boolean; }; export const getFieldsDropdownItems = ( { fields, layout }: FieldsInfo, { subTableCode, // specified subTable baseFilter, // set not allowed items hidden, undefined means no filter defaultLabel, // label shown (default '--------') filterType, // set not allowed items disabled, undefined and [] means no filter dependFilterField, // used for filterType defaultDisableCallback, // callback to control disabled items, like filterType needAllSubTableField = false, // show all subtable fields }: Param, ) => { // get used field codes const fieldOrder = extractFields(layout, baseFilter, !!needAllSubTableField, subTableCode); const fieldMap = fields; // create labels const labels: ComboboxItem[] = [ { value: EMPTY_OPTION.value, label: defaultLabel || EMPTY_OPTION.label, }, ]; return fieldOrder.reduce((acc, fieldCode) => { const field = fieldMap[fieldCode]; if (!fieldCode) return acc; acc.push({ value: fieldCode, label: field.label + '(FC: ' + fieldCode + ')', disabled: (defaultDisableCallback && defaultDisableCallback(field)) || (filterType && !checkFilterType(field, dependFilterField, filterType)), }); return acc; }, labels); }; const checkFilterType = (field: OneOf, dependFilterField: OneOf | undefined, filterType: FilterType) => { if (!filterType.length) return true; // [] means no filter return !!filterType.find((type) => { if (isSpecialType(type)) { return type.check(field, dependFilterField); } return isType[type](field); }); }; export const getTableFieldsDropdownItems = ({ fields }: FieldsInfo, filterType?: FieldType) => { return Object.keys(fields).reduce( (acc, fieldCode) => { const field = fields[fieldCode]; if (filterType && !isType[filterType](field)) return acc; acc.push({ value: fieldCode, label: field.label + '(FC: ' + fieldCode + ')', }); return acc; }, [EMPTY_OPTION], ); }; const extractFields = (layout: Layout, baseFilter: FieldType[] | undefined, needAllSubTableField: boolean, subTableCode?: string) => { return layout.reduce((acc, each) => { if (each.type === 'GROUP') { acc.push(...extractFields(each.layout, baseFilter, needAllSubTableField, subTableCode)); } else if (each.type === 'ROW' || (!needAllSubTableField && each.code === subTableCode) || (needAllSubTableField && each.type === 'SUBTABLE')) { acc.push( ...each.fields.map((field) => { if (!('code' in field)) return ''; if (!baseFilter) return field.code; return baseFilter.find((t) => t === field.type) ? field?.code || '' : ''; }), ); } return acc; }, [] as string[]); }; export function getFieldObj(fieldCode: string, { fields }: FieldsInfo, subTableCode?: string) { const meta = getMeta(fields, subTableCode); return meta[fieldCode]; } export function getMeta(fields: Properties, subTableCode?: string, withNoSubTableField = true) { if (!fields || !subTableCode) { return fields; } let meta = fields; const table = meta[subTableCode]; if (isType.SUBTABLE(table)) { const subFields = table.fields; Object.values(subFields).forEach((field) => { if (typeof field === 'object' && field !== null) { (field as Record).subField = true; } }); if (withNoSubTableField) { meta = { ...fields, ...subFields }; } else { meta = subFields; } } return meta; } export const isStringArray = (value: any) => { if (Array.isArray(value) && value.every((x) => typeof x === 'string')) { return true; } return false; };