diff --git a/src/main/kintone-api.ts b/src/main/kintone-api.ts index f87a7cd..0fd5865 100644 --- a/src/main/kintone-api.ts +++ b/src/main/kintone-api.ts @@ -2,13 +2,14 @@ import { KintoneRestAPIClient } from "@kintone/rest-api-client"; import type { KintoneRestAPIError } from "@kintone/rest-api-client"; import type { DomainWithPassword } from "@shared/types/domain"; import type { - KintoneApp, + AppResponse, + AppsResponse, + AppCustomizeResponse, AppDetail, FileContent, AppCustomizationConfig, KintoneApiError, - JSFileConfig, - CSSFileConfig, + FileConfig, } from "@shared/types/kintone"; /** @@ -32,12 +33,6 @@ export class KintoneError extends Error { } } -// Use typeof to get SDK method return types -type KintoneClient = KintoneRestAPIClient; -type AppResponse = ReturnType; -type AppsResponse = ReturnType; -type AppCustomizeResponse = ReturnType; - /** * Kintone REST API Client */ @@ -87,48 +82,24 @@ export class SelfKintoneClient { } } - - private mapApp(app: Awaited): KintoneApp { - return { - appId: app.appId, - name: app.name, - code: app.code, - spaceId: app.spaceId || undefined, - createdAt: app.createdAt, - creator: app.creator, - modifiedAt: app.modifiedAt, - modifier: app.modifier, - }; - } - private mapResource( resource: | Awaited["desktop"]["js"][number] | Awaited["desktop"]["css"][number], - ): JSFileConfig | CSSFileConfig { - if (resource.type === "FILE") { - return { - type: "FILE", - file: { - fileKey: resource.file.fileKey, - name: resource.file.name, - size: parseInt(resource.file.size, 10), - }, - }; - } - return { - type: "URL", - url: resource.url, - }; + ): FileConfig { + // Return SDK type directly - no conversion needed + return resource; } private buildCustomizeSection( - js?: JSFileConfig[], - css?: CSSFileConfig[], - ): Parameters[0]["desktop"] { + js?: FileConfig[], + css?: FileConfig[], + ): Parameters< + KintoneRestAPIClient["app"]["updateAppCustomize"] + >[0]["desktop"] { if (!js && !css) return undefined; - const mapItem = (item: JSFileConfig | CSSFileConfig) => { + const mapItem = (item: FileConfig) => { if (item.type === "FILE" && item.file) { return { type: "FILE" as const, file: { fileKey: item.file.fileKey } }; } @@ -150,7 +121,7 @@ export class SelfKintoneClient { async getApps(options?: { limit?: number; offset?: number; - }): Promise { + }): Promise { return this.withErrorHandling(async () => { // If pagination options provided, use them directly if (options?.limit !== undefined || options?.offset !== undefined) { @@ -158,11 +129,11 @@ export class SelfKintoneClient { if (options.limit) params.limit = options.limit; if (options.offset) params.offset = options.offset; const response = await this.client.app.getApps(params); - return response.apps.map((app) => this.mapApp(app)); + return response.apps; } // Otherwise, fetch all apps (pagination handled internally) - const allApps: Awaited["apps"] = []; + const allApps: AppResponse[] = []; const limit = 100; // Max allowed by Kintone API let offset = 0; let hasMore = true; @@ -179,7 +150,7 @@ export class SelfKintoneClient { } } - return allApps.map((app) => this.mapApp(app)); + return allApps; }); } @@ -203,7 +174,6 @@ export class SelfKintoneClient { mobile: customizeInfo.mobile.css?.map((css) => this.mapResource(css)) || [], }, - plugins: [], }; return { diff --git a/src/preload/index.d.ts b/src/preload/index.d.ts index fde3def..ecd3575 100644 --- a/src/preload/index.d.ts +++ b/src/preload/index.d.ts @@ -16,9 +16,10 @@ import type { } from "@shared/types/ipc"; import type { Domain, DomainWithStatus } from "@shared/types/domain"; import type { - KintoneApp, + AppResponse, AppDetail, FileContent, + KintoneSpace, } from "@shared/types/kintone"; import type { Version } from "@shared/types/version"; @@ -39,10 +40,13 @@ export interface SelfAPI { updateDomain: (params: UpdateDomainParams) => Promise>; deleteDomain: (id: string) => Promise>; testConnection: (id: string) => Promise>; - testDomainConnection: (params: TestDomainConnectionParams) => Promise>; + testDomainConnection: ( + params: TestDomainConnectionParams, + ) => Promise>; // ==================== Browse ==================== - getApps: (params: GetAppsParams) => Promise>; + getSpaces: (params: { domainId: string }) => Promise>; + getApps: (params: GetAppsParams) => Promise>; getAppDetail: (params: GetAppDetailParams) => Promise>; getFileContent: ( params: GetFileContentParams, diff --git a/src/renderer/src/components/AppDetail/AppDetail.tsx b/src/renderer/src/components/AppDetail/AppDetail.tsx index 8e6d7c4..6d376e9 100644 --- a/src/renderer/src/components/AppDetail/AppDetail.tsx +++ b/src/renderer/src/components/AppDetail/AppDetail.tsx @@ -24,7 +24,7 @@ import { createStyles } from "antd-style"; import { useAppStore } from "@renderer/stores"; import { useDomainStore } from "@renderer/stores"; import { CodeViewer } from "../CodeViewer"; -import { JSFileConfig, CSSFileConfig } from "@shared/types/kintone"; +import { FileConfig } from "@shared/types/kintone"; const useStyles = createStyles(({ token, css }) => ({ container: css` @@ -168,7 +168,7 @@ const AppDetail: React.FC = () => { } const renderFileList = ( - files: (JSFileConfig | CSSFileConfig)[] | undefined, + files: (FileConfig)[] | undefined, type: "js" | "css", ) => { if (!files || files.length === 0) { diff --git a/src/renderer/src/stores/appStore.ts b/src/renderer/src/stores/appStore.ts index 90df3e0..91ea77d 100644 --- a/src/renderer/src/stores/appStore.ts +++ b/src/renderer/src/stores/appStore.ts @@ -6,11 +6,11 @@ import { create } from "zustand"; import { persist } from "zustand/middleware"; -import type { KintoneApp, AppDetail } from "@shared/types/kintone"; +import type { AppResponse, AppDetail } from "@shared/types/kintone"; interface AppState { // State - apps: KintoneApp[]; + apps: AppResponse[]; currentApp: AppDetail | null; selectedAppId: string | null; loading: boolean; @@ -27,7 +27,7 @@ interface AppState { loadedAt: string | null; // Actions - setApps: (apps: KintoneApp[]) => void; + setApps: (apps: AppResponse[]) => void; setCurrentApp: (app: AppDetail | null) => void; setSelectedAppId: (id: string | null) => void; setLoading: (loading: boolean) => void; diff --git a/src/shared/types/ipc.ts b/src/shared/types/ipc.ts index d8eea72..890a7cc 100644 --- a/src/shared/types/ipc.ts +++ b/src/shared/types/ipc.ts @@ -5,8 +5,8 @@ import type { Domain, DomainWithPassword, DomainWithStatus } from "./domain"; import type { + AppResponse, KintoneSpace, - KintoneApp, AppDetail, FileContent, } from "./kintone"; @@ -136,11 +136,13 @@ export interface ElectronAPI { updateDomain: (params: UpdateDomainParams) => Promise>; deleteDomain: (id: string) => Promise>; testConnection: (id: string) => Promise>; - testDomainConnection: (params: TestDomainConnectionParams) => Promise>; + testDomainConnection: ( + params: TestDomainConnectionParams, + ) => Promise>; // Browse getSpaces: (params: GetSpacesParams) => Promise>; - getApps: (params: GetAppsParams) => Promise>; + getApps: (params: GetAppsParams) => Promise>; getAppDetail: (params: GetAppDetailParams) => Promise>; getFileContent: ( params: GetFileContentParams, diff --git a/src/shared/types/kintone.ts b/src/shared/types/kintone.ts index 3c7ed80..d08358f 100644 --- a/src/shared/types/kintone.ts +++ b/src/shared/types/kintone.ts @@ -1,100 +1,45 @@ /** * Kintone API response types - * Based on REQUIREMENTS.md:331-345 + * Using SDK types where possible, custom types only for business-layer aggregation */ -// Space types -export interface KintoneSpace { - id: string; - name: string; - code: string; - createdAt?: string; - creator?: { - code: string; - name: string; - }; -} +import type { KintoneRestAPIClient } from "@kintone/rest-api-client"; -// App types -export interface KintoneApp { - appId: string; - name: string; - code?: string; - spaceId?: string; - createdAt: string; - creator?: { - code: string; - name: string; - }; - modifiedAt?: string; - modifier?: { - code: string; - name: string; - }; -} +// ==================== SDK Type Extraction ==================== +// Use typeof + ReturnType to extract types from SDK methods +type KintoneClient = KintoneRestAPIClient; -// File configuration types -export interface JSFileConfig { - type: "FILE" | "URL"; - file?: { - fileKey: string; - name?: string; // Optional for deploy, filled by Kintone - size?: number; // Optional for deploy, filled by Kintone - }; - url?: string; -} +/** App response from getApp/getApps */ +export type AppResponse = Awaited>; -export interface CSSFileConfig { - type: "FILE" | "URL"; - file?: { - fileKey: string; - name?: string; // Optional for deploy, filled by Kintone - size?: number; // Optional for deploy, filled by Kintone - }; - url?: string; -} +/** Apps list response */ +export type AppsResponse = Awaited>; -// App customization config -export interface AppCustomizationConfig { - javascript?: { - pc?: JSFileConfig[]; - mobile?: JSFileConfig[]; - }; - stylesheet?: { - pc?: CSSFileConfig[]; - mobile?: CSSFileConfig[]; - }; - plugins?: AppPlugin[]; -} +/** App customization response */ +export type AppCustomizeResponse = Awaited< + ReturnType +>; -export interface AppPlugin { - id: string; - name: string; - enabled: boolean; -} +/** File upload response */ +export type FileUploadResponse = Awaited< + ReturnType +>; -// App detail response -export interface AppDetail { - appId: string; - name: string; - code?: string; - description?: string; - spaceId?: string; - spaceName?: string; - createdAt: string; - creator: { - code: string; - name: string; - }; - modifiedAt: string; - modifier: { - code: string; - name: string; - }; +// ==================== Custom Business Types ==================== +// These types represent business-layer aggregation or additional metadata + +/** + * App detail - combines app info + customization + * This is a business-layer type that aggregates data from multiple API calls + */ +export interface AppDetail extends AppResponse { customization?: AppCustomizationConfig; } -// File content +/** + * File content - file metadata + content + * SDK's downloadFile only returns ArrayBuffer, we add metadata + */ export interface FileContent { fileKey: string; name: string; @@ -103,7 +48,31 @@ export interface FileContent { content?: string; // Base64 encoded or text } -// API Error +/** + * App customization config + * Uses SDK's customize types structure (desktop/mobile) but simplified for internal use + * Note: SDK uses { js: [], css: [] } but we use { javascript: { pc, mobile } } for easier UI binding + */ +export interface AppCustomizationConfig { + javascript?: { + pc?: FileConfig[]; + mobile?: FileConfig[]; + }; + stylesheet?: { + pc?: FileConfig[]; + mobile?: FileConfig[]; + }; +} + +/** + * File config for customization + * Using SDK's type directly from AppCustomizeResponse + */ +export type FileConfig = Awaited["desktop"]["js"][number]; + +/** + * API Error - simplified from SDK's KintoneRestAPIError + */ export interface KintoneApiError { code: string; message: string;