remove kintone-pretty-fields library dependency.
This commit is contained in:
49
package-lock.json
generated
49
package-lock.json
generated
@ -12,9 +12,8 @@
|
|||||||
"angular-expressions": "^1.4.3",
|
"angular-expressions": "^1.4.3",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"core-js": "^3.42.0",
|
"core-js": "^3.42.0",
|
||||||
"docxtemplater": "^3.63.0",
|
"docxtemplater": "^3.63.2",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"kintone-pretty-fields": "^0.10.5",
|
|
||||||
"moize": "^6.1.6",
|
"moize": "^6.1.6",
|
||||||
"pizzip": "^3.2.0",
|
"pizzip": "^3.2.0",
|
||||||
"react": "^19.1.0",
|
"react": "^19.1.0",
|
||||||
@ -32,7 +31,7 @@
|
|||||||
"@shin-chan/kypes": "^0.0.7",
|
"@shin-chan/kypes": "^0.0.7",
|
||||||
"@swc/helpers": "^0.5.17",
|
"@swc/helpers": "^0.5.17",
|
||||||
"@types/file-saver": "^2.0.7",
|
"@types/file-saver": "^2.0.7",
|
||||||
"@types/react": "^19.1.5",
|
"@types/react": "^19.1.6",
|
||||||
"@types/react-dom": "^19.1.5",
|
"@types/react-dom": "^19.1.5",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"css-loader": "^7.1.2",
|
"css-loader": "^7.1.2",
|
||||||
@ -1633,9 +1632,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/react": {
|
"node_modules/@types/react": {
|
||||||
"version": "19.1.5",
|
"version": "19.1.6",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.6.tgz",
|
||||||
"integrity": "sha512-piErsCVVbpMMT2r7wbawdZsq4xMvIAhQuac2gedQHysu1TZYEigE6pnFfgZT+/jQnrRuF5r+SHzuehFjfRjr4g==",
|
"integrity": "sha512-JeG0rEWak0N6Itr6QUx+X60uQmN+5t3j9r/OVDtWzFXKaj6kD1BwJzOksD0FF6iWxZlbE1kB0q9vtnU2ekqa1Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -4016,9 +4015,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/docxtemplater": {
|
"node_modules/docxtemplater": {
|
||||||
"version": "3.63.0",
|
"version": "3.63.2",
|
||||||
"resolved": "https://registry.npmjs.org/docxtemplater/-/docxtemplater-3.63.0.tgz",
|
"resolved": "https://registry.npmjs.org/docxtemplater/-/docxtemplater-3.63.2.tgz",
|
||||||
"integrity": "sha512-ePkPXWBxcHxmja4mGY6qqqzRPHL6KnWxP+6T3xry9mrA1SNUKrQB2GO4JQSMSmGTNmgxuStBQ5pSG+ge+met8Q==",
|
"integrity": "sha512-e/euKWiDeEXWB7g+eUAkZEduRp/Tf+kqpHO5KHindzAuBht/9q7oEOaOKQo5RCtSMCYjAZj+jXdXKPUPR6XIeQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@xmldom/xmldom": "^0.9.8"
|
"@xmldom/xmldom": "^0.9.8"
|
||||||
@ -4027,18 +4026,6 @@
|
|||||||
"node": ">=0.10"
|
"node": ">=0.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/dotenv": {
|
|
||||||
"version": "16.5.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz",
|
|
||||||
"integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==",
|
|
||||||
"license": "BSD-2-Clause",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=12"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://dotenvx.com"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/dunder-proto": {
|
"node_modules/dunder-proto": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||||
@ -6899,26 +6886,6 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/kintone-pretty-fields": {
|
|
||||||
"version": "0.10.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/kintone-pretty-fields/-/kintone-pretty-fields-0.10.5.tgz",
|
|
||||||
"integrity": "sha512-5vstgXuakpxzLVv7np72ot9FzjENS9BwE2xjBpxoRJmqoCrbdj4Vszr8KVe4gZdTxzhlvg0Ssfig87zXLNnjXg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@kintone/rest-api-client": "^5.5.2",
|
|
||||||
"kintone-typeguard": "^0.15.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/kintone-typeguard": {
|
|
||||||
"version": "0.15.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/kintone-typeguard/-/kintone-typeguard-0.15.2.tgz",
|
|
||||||
"integrity": "sha512-QFIrk3bKd0+vGgz7VRPWvl4fVw3Qrqgi7mr6S0/qPUUyrIDy3IZjxBbtcEUUr9bGmCefHZchQ8ZU49XgurRmSA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@kintone/rest-api-client": "^5.5.2",
|
|
||||||
"dotenv": "^16.4.5"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/language-subtag-registry": {
|
"node_modules/language-subtag-registry": {
|
||||||
"version": "0.3.23",
|
"version": "0.3.23",
|
||||||
"resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz",
|
"resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz",
|
||||||
|
@ -16,9 +16,8 @@
|
|||||||
"angular-expressions": "^1.4.3",
|
"angular-expressions": "^1.4.3",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"core-js": "^3.42.0",
|
"core-js": "^3.42.0",
|
||||||
"docxtemplater": "^3.63.0",
|
"docxtemplater": "^3.63.2",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"kintone-pretty-fields": "^0.10.5",
|
|
||||||
"moize": "^6.1.6",
|
"moize": "^6.1.6",
|
||||||
"pizzip": "^3.2.0",
|
"pizzip": "^3.2.0",
|
||||||
"react": "^19.1.0",
|
"react": "^19.1.0",
|
||||||
@ -36,7 +35,7 @@
|
|||||||
"@shin-chan/kypes": "^0.0.7",
|
"@shin-chan/kypes": "^0.0.7",
|
||||||
"@swc/helpers": "^0.5.17",
|
"@swc/helpers": "^0.5.17",
|
||||||
"@types/file-saver": "^2.0.7",
|
"@types/file-saver": "^2.0.7",
|
||||||
"@types/react": "^19.1.5",
|
"@types/react": "^19.1.6",
|
||||||
"@types/react-dom": "^19.1.5",
|
"@types/react-dom": "^19.1.5",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"css-loader": "^7.1.2",
|
"css-loader": "^7.1.2",
|
||||||
|
@ -2,3 +2,6 @@ import invariant from 'tiny-invariant';
|
|||||||
|
|
||||||
export const PLUGIN_ID = kintone.$PLUGIN_ID;
|
export const PLUGIN_ID = kintone.$PLUGIN_ID;
|
||||||
invariant(PLUGIN_ID, 'The PLUGIN_ID is not available. Please ensure you are on a Kintone plugin page.');
|
invariant(PLUGIN_ID, 'The PLUGIN_ID is not available. Please ensure you are on a Kintone plugin page.');
|
||||||
|
|
||||||
|
export const LANGUAGE = kintone.getLoginUser().language;
|
||||||
|
invariant(LANGUAGE, 'The LANGUAGE is not available. Please ensure you are on a Kintone plugin page.');
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { KintoneRestAPIClient } from '@kintone/rest-api-client';
|
import { KintoneRestAPIClient } from '@kintone/rest-api-client';
|
||||||
import { kintonePrettyFields } from 'kintone-pretty-fields';
|
import { Properties as KintoneFormFieldProperties } from '@kintone/rest-api-client/lib/src/client/types';
|
||||||
import moize from 'moize';
|
import moize from 'moize';
|
||||||
import invariant from 'tiny-invariant';
|
import invariant from 'tiny-invariant';
|
||||||
import { PLUGIN_ID } from '../common/global';
|
import { PLUGIN_ID } from '../common/global';
|
||||||
@ -16,28 +16,30 @@ import KintonePluginTitle from '../common/ui/KintonePluginTitle';
|
|||||||
|
|
||||||
import styles from './Settings.module.css';
|
import styles from './Settings.module.css';
|
||||||
|
|
||||||
const cachedFields = moize.promise(async (appId: number) => {
|
const cachedFormFieldsProperties = moize.promise(async (appId: number): Promise<KintoneFormFieldProperties> => {
|
||||||
const client = new KintoneRestAPIClient();
|
const client = new KintoneRestAPIClient();
|
||||||
const fields = await kintonePrettyFields.getFields({ client, app: appId, lang: 'en', preview: false });
|
const { properties } = await client.app.getFormFields({ app: appId, lang: 'en', preview: false });
|
||||||
return fields;
|
return properties;
|
||||||
});
|
});
|
||||||
|
|
||||||
const Settings: React.FC = () => {
|
const Settings: React.FC = () => {
|
||||||
const config = kintone.plugin.app.getConfig(PLUGIN_ID);
|
const config = kintone.plugin.app.getConfig(PLUGIN_ID);
|
||||||
const appId = kintone.app.getId();
|
const appId = kintone.app.getId();
|
||||||
invariant(appId, 'The app ID is not available. Please ensure you are on a Kintone app page.');
|
invariant(appId, 'The app ID is not available. Please ensure you are on a Kintone app page.');
|
||||||
const { fields } = React.use(cachedFields(appId));
|
const properties = React.use(cachedFormFieldsProperties(appId));
|
||||||
const fileFields = fields.filter(kintonePrettyFields.isFile);
|
const fileFields = Object.values(properties).filter((property) => property.type === 'FILE');
|
||||||
const options: KintonePluginSelectOptionData[] = [
|
const options: KintonePluginSelectOptionData[] = [
|
||||||
{ key: '-', value: '', label: 'Select a File field', disabled: true }, // Default option
|
{ key: '-', value: '', label: 'Select a File field', disabled: true }, // Default option
|
||||||
...fileFields.map((field) => ({
|
...fileFields.map((property) => ({
|
||||||
key: field.code,
|
key: property.code,
|
||||||
value: field.code,
|
value: property.code,
|
||||||
label: field.label,
|
label: property.label,
|
||||||
})),
|
})),
|
||||||
];
|
];
|
||||||
|
|
||||||
const [template, setTemplate] = React.useState<string>(config.template ?? '');
|
const [template, setTemplate] = React.useState<string>(
|
||||||
|
() => options.find((option) => option.value === (config.template ?? ''))?.value ?? '',
|
||||||
|
);
|
||||||
|
|
||||||
const handleOnSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
const handleOnSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { KintoneRestAPIClient } from '@kintone/rest-api-client';
|
import { KintoneRestAPIClient } from '@kintone/rest-api-client';
|
||||||
|
import { Properties as KintoneFormFieldProperties } from '@kintone/rest-api-client/lib/src/client/types';
|
||||||
import { KintoneRecord } from '@shin-chan/kypes/types/page';
|
import { KintoneRecord } from '@shin-chan/kypes/types/page';
|
||||||
import Docxtemplater from 'docxtemplater';
|
import Docxtemplater from 'docxtemplater';
|
||||||
import expressionParser from 'docxtemplater/expressions';
|
import expressionParser from 'docxtemplater/expressions';
|
||||||
import { saveAs } from 'file-saver';
|
import { saveAs } from 'file-saver';
|
||||||
|
import moize from 'moize';
|
||||||
import PizZip from 'pizzip';
|
import PizZip from 'pizzip';
|
||||||
import { PLUGIN_ID } from '../common/global';
|
import invariant from 'tiny-invariant';
|
||||||
|
import { LANGUAGE, PLUGIN_ID } from '../common/global';
|
||||||
import KintonePluginAlert from '../common/ui/KintonePluginAlert';
|
import KintonePluginAlert from '../common/ui/KintonePluginAlert';
|
||||||
import KintonePluginButton from '../common/ui/KintonePluginButton';
|
import KintonePluginButton from '../common/ui/KintonePluginButton';
|
||||||
|
|
||||||
@ -22,15 +25,23 @@ interface MenuPanelProps {
|
|||||||
event: kintone.events.AppRecordDetailShowEvent | kintone.events.MobileAppRecordDetailShowEvent;
|
event: kintone.events.AppRecordDetailShowEvent | kintone.events.MobileAppRecordDetailShowEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
const record2data = (record: Partial<KintoneRecord>): TemplateData => {
|
const cachedFormFieldsProperties = moize.promise(async (appId: number): Promise<KintoneFormFieldProperties> => {
|
||||||
|
const client = new KintoneRestAPIClient();
|
||||||
|
const { properties } = await client.app.getFormFields({ app: appId, lang: 'en', preview: false });
|
||||||
|
return properties;
|
||||||
|
});
|
||||||
|
|
||||||
|
const record2data = (properties: KintoneFormFieldProperties, record: Partial<KintoneRecord>): TemplateData => {
|
||||||
const data: TemplateData = {};
|
const data: TemplateData = {};
|
||||||
for (const key in record) {
|
for (const key in record) {
|
||||||
if (Object.prototype.hasOwnProperty.call(record, key)) {
|
if (Object.prototype.hasOwnProperty.call(record, key) && Object.prototype.hasOwnProperty.call(properties, key)) {
|
||||||
const field = record[key];
|
const item = record[key];
|
||||||
if (field == null) continue;
|
const property = properties[key];
|
||||||
const { type, value } = field;
|
if (item == null) continue;
|
||||||
if (value == null) continue;
|
const { type, value } = item;
|
||||||
if (type === 'CREATOR' || type === 'MODIFIER') {
|
if (value == null) {
|
||||||
|
data[key] = '';
|
||||||
|
} else if (type === 'CREATOR' || type === 'MODIFIER') {
|
||||||
data[key] = {
|
data[key] = {
|
||||||
name: value.name,
|
name: value.name,
|
||||||
code: value.code,
|
code: value.code,
|
||||||
@ -47,7 +58,45 @@ const record2data = (record: Partial<KintoneRecord>): TemplateData => {
|
|||||||
return { name: v.name, code: v.code };
|
return { name: v.name, code: v.code };
|
||||||
});
|
});
|
||||||
} else if (type === 'SUBTABLE') {
|
} else if (type === 'SUBTABLE') {
|
||||||
data[key] = value.map((subRecord) => record2data(subRecord.value));
|
invariant(property.type === 'SUBTABLE', `Expected property type to be SUBTABLE, but got ${property.type}`);
|
||||||
|
data[key] = value.map((subRecord) => record2data(property.fields, subRecord.value));
|
||||||
|
} else if (type === 'CALC') {
|
||||||
|
invariant(property.type === 'CALC', `Expected property type to be CALC, but got ${property.type}`);
|
||||||
|
if (value === '') {
|
||||||
|
data[key] = '';
|
||||||
|
} else if (property.format === 'NUMBER_DIGIT') {
|
||||||
|
// "1234567" -> "1,234,567"
|
||||||
|
data[key] = Number(value).toLocaleString('en-US');
|
||||||
|
} else if (property.format === 'HOUR_MINUTE') {
|
||||||
|
// "49:30" -> en:"49 hours 30 minutes", ja: "49時間30分"
|
||||||
|
const [hours, minutes] = value.split(':').map((v) => Number(v));
|
||||||
|
invariant(
|
||||||
|
!isNaN(hours) && !isNaN(minutes),
|
||||||
|
`Expected hours and minutes to be numbers, but got hours: ${hours}, minutes: ${minutes}`,
|
||||||
|
);
|
||||||
|
if (LANGUAGE === 'ja') {
|
||||||
|
data[key] = `${hours}時間${minutes}分`;
|
||||||
|
} else {
|
||||||
|
data[key] = `${hours} hours ${minutes} minutes`;
|
||||||
|
}
|
||||||
|
} else if (property.format === 'DAY_HOUR_MINUTE') {
|
||||||
|
// "49:30" -> en: "2 days 1 hour 30 minutes", ja: "2日1時間30分
|
||||||
|
const [hours, minutes] = value.split(':').map((v) => Number(v));
|
||||||
|
invariant(
|
||||||
|
!isNaN(hours) && !isNaN(minutes),
|
||||||
|
`Expected hours and minutes to be numbers, but got hours: ${hours}, minutes: ${minutes}`,
|
||||||
|
);
|
||||||
|
const days = Math.floor(Number(hours) / 24);
|
||||||
|
const remainingHours = Number(hours) % 24;
|
||||||
|
if (LANGUAGE === 'ja') {
|
||||||
|
data[key] = `${days}日${remainingHours}時間${minutes}分`;
|
||||||
|
} else {
|
||||||
|
data[key] = `${days} days ${remainingHours} hours ${minutes} minutes`;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// NUMBER, DATETIME, DATE, TIME
|
||||||
|
data[key] = value;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
data[key] = value;
|
data[key] = value;
|
||||||
}
|
}
|
||||||
@ -58,6 +107,8 @@ const record2data = (record: Partial<KintoneRecord>): TemplateData => {
|
|||||||
|
|
||||||
const MenuPanel: React.FC<MenuPanelProps> = (props) => {
|
const MenuPanel: React.FC<MenuPanelProps> = (props) => {
|
||||||
const { event } = props;
|
const { event } = props;
|
||||||
|
const appId = event.appId;
|
||||||
|
const properties = React.use(cachedFormFieldsProperties(appId));
|
||||||
const config = kintone.plugin.app.getConfig(PLUGIN_ID);
|
const config = kintone.plugin.app.getConfig(PLUGIN_ID);
|
||||||
const template: string = config.template ?? '';
|
const template: string = config.template ?? '';
|
||||||
if (template === '') {
|
if (template === '') {
|
||||||
@ -94,11 +145,6 @@ const MenuPanel: React.FC<MenuPanelProps> = (props) => {
|
|||||||
}
|
}
|
||||||
const handleOnClickOutputButton = (e: React.MouseEvent<HTMLButtonElement>) => {
|
const handleOnClickOutputButton = (e: React.MouseEvent<HTMLButtonElement>) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const appId = event.type === 'app.record.detail.show' ? kintone.app.getId() : kintone.mobile.app.getId();
|
|
||||||
if (!appId) {
|
|
||||||
alert('The app ID is not available. Please ensure you are on a Kintone app page.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const client = new KintoneRestAPIClient();
|
const client = new KintoneRestAPIClient();
|
||||||
client.file
|
client.file
|
||||||
.downloadFile({ fileKey })
|
.downloadFile({ fileKey })
|
||||||
@ -109,7 +155,7 @@ const MenuPanel: React.FC<MenuPanelProps> = (props) => {
|
|||||||
linebreaks: true,
|
linebreaks: true,
|
||||||
parser: expressionParser,
|
parser: expressionParser,
|
||||||
});
|
});
|
||||||
doc.render(record2data(event.record));
|
doc.render(record2data(properties, event.record));
|
||||||
const out = doc.getZip().generate({ type: 'blob', mimeType: DOCX_CONTENTTYPE });
|
const out = doc.getZip().generate({ type: 'blob', mimeType: DOCX_CONTENTTYPE });
|
||||||
saveAs(out, 'output.docx');
|
saveAs(out, 'output.docx');
|
||||||
})
|
})
|
||||||
|
Reference in New Issue
Block a user