diff --git a/apps/web/src/app/(appListNavigation)/app/chart/[id]/page.tsx b/apps/web/src/app/(appListNavigation)/app/chart/[id]/page.tsx index 64e65532..e8a3746e 100644 --- a/apps/web/src/app/(appListNavigation)/app/chart/[id]/page.tsx +++ b/apps/web/src/app/(appListNavigation)/app/chart/[id]/page.tsx @@ -1,5 +1,7 @@ import { ShowChart } from "@pageComponents/showChart"; -const Page = () => ; +const Page = ({ params: { id } }: { params: { id: string } }) => ( + +); export default Page; diff --git a/apps/web/src/pageComponents/showChart/components/ShowChartServer.tsx b/apps/web/src/pageComponents/showChart/components/ShowChartServer.tsx index 854bb3f9..6bc76b7b 100644 --- a/apps/web/src/pageComponents/showChart/components/ShowChartServer.tsx +++ b/apps/web/src/pageComponents/showChart/components/ShowChartServer.tsx @@ -1,16 +1,41 @@ +import { SummaryCriteria } from "@oneforall/domain/schema/summary/sumRecordsSchema"; +import { createDataArray } from "@pageComponents/showChart/components/createDataArray"; import ShowChartClient from "@pageComponents/showChart/components/ShowChartClient"; +import { fetchQuery } from "@persistence/database/server/fetchQuery"; +import { parseToRecords } from "@v3/graphql/public/convert/parseToRecords"; +import { GetAppDocument } from "@v3/graphql/public/type"; -export const ShowChartServer = async () => { - const data = [ - { name: "Group A", value: 400 }, - { name: "Group B", value: 300 }, - { name: "Group C", value: 300 }, - { name: "Group D", value: 200 }, - { name: "Group E", value: 278 }, - { name: "Group F", value: 189 }, - ]; +export const ShowChartServer = async ({ appId }: { appId: string }) => { + const { data } = await fetchQuery(GetAppDocument, { appId }); + const records = parseToRecords(data?.app?.records ?? []); + + const criteria: SummaryCriteria = { + groupingFields: { + "1718289203212": { + id: "1718289203212", + fieldName: "値", + fieldKind: "text", + fieldIndex: 1, + options: {}, + }, + }, + summaryFields: { + "1718289203213": { + id: "1718289203213", + fieldName: "数値", + fieldKind: "numeric", + fieldIndex: 2, + options: { + thousandsSeparatorPosition: 3, + }, + }, + }, + }; + criteria; + + const dataArray = createDataArray({ records, criteria }); const colors = ["#0088FE", "#00C49F", "#FFBB28", "#FF8042"]; - return ; + return ; }; diff --git a/apps/web/src/pageComponents/showChart/components/createDataArray.test.ts b/apps/web/src/pageComponents/showChart/components/createDataArray.test.ts new file mode 100644 index 00000000..4e220ca2 --- /dev/null +++ b/apps/web/src/pageComponents/showChart/components/createDataArray.test.ts @@ -0,0 +1,104 @@ +import { Records } from "@oneforall/domain/schema/recordSchema"; +import { SummaryCriteria } from "@oneforall/domain/schema/summary/sumRecordsSchema"; +import { createDataArray } from "@pageComponents/showChart/components/createDataArray"; + +describe("createDataArray", () => { + it("", () => { + const criteria: SummaryCriteria = { + groupingFields: { + f1: { + id: "f1", + fieldName: "値", + fieldKind: "text", + fieldIndex: 1, + options: {}, + }, + }, + summaryFields: { + f2: { + id: "f2", + fieldName: "数値", + fieldKind: "numeric", + fieldIndex: 2, + options: { + thousandsSeparatorPosition: 3, + }, + }, + }, + }; + + const records: Records = { + r1: { + recordId: "1718289218434", + isEditing: false, + columns: { + f1: { + fieldKind: "text", + value: "あああ", + }, + f2: { + fieldKind: "numeric", + value: "100", + }, + }, + }, + r2: { + recordId: "1718289227545", + isEditing: false, + columns: { + f1: { + fieldKind: "text", + value: "あああ", + }, + f2: { + fieldKind: "numeric", + value: "200", + }, + }, + }, + r3: { + recordId: "1718289236639", + isEditing: false, + columns: { + f1: { + fieldKind: "text", + value: "いいい", + }, + f2: { + fieldKind: "numeric", + value: "200", + }, + }, + }, + r4: { + recordId: "1718895572486", + isEditing: false, + columns: { + f1: { + fieldKind: "text", + value: "ううう", + }, + f2: { + fieldKind: "numeric", + value: "500", + }, + }, + }, + }; + + expect(createDataArray({ records, criteria })).toEqual([ + { + name: "あああ", + value: 300, + }, + { + name: "いいい", + value: 200, + }, + { + name: "ううう", + value: 500, + }, + ]); + }); +}); diff --git a/apps/web/src/pageComponents/showChart/components/createDataArray.ts b/apps/web/src/pageComponents/showChart/components/createDataArray.ts new file mode 100644 index 00000000..14028b96 --- /dev/null +++ b/apps/web/src/pageComponents/showChart/components/createDataArray.ts @@ -0,0 +1,18 @@ +import { sumRecords } from "@oneforall/domain/convert/summary/sumRecords"; +import { Records } from "@oneforall/domain/schema/recordSchema"; +import { SummaryCriteria } from "@oneforall/domain/schema/summary/sumRecordsSchema"; + +export const createDataArray = ({ + records, + criteria, +}: { + records: Records; + criteria: SummaryCriteria; +}) => { + return Object.values(sumRecords(records, criteria)).map((record) => { + const name: string = Object.values(record.columns)[0]?.value ?? ""; + const value: number = record.sum; + + return { name, value }; + }); +}; diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json index b8dc3d6c..a86c0f5f 100644 --- a/apps/web/tsconfig.json +++ b/apps/web/tsconfig.json @@ -1,7 +1,8 @@ { "extends": "@acme/tsconfig/base.json", "compilerOptions": { - "target": "es5", + "target": "es2015", + "downlevelIteration": true, "lib": [ "dom", "dom.iterable", diff --git a/packages/domain/convert/summary/sumRecords.test.ts b/packages/domain/convert/summary/sumRecords.test.ts new file mode 100644 index 00000000..16164ce5 --- /dev/null +++ b/packages/domain/convert/summary/sumRecords.test.ts @@ -0,0 +1,123 @@ +import { Records } from "../../schema/recordSchema"; +import { + SummaryCriteria, + SumRecords, +} from "../../schema/summary/sumRecordsSchema"; +import { sumRecords } from "./sumRecords"; + +describe("sumRecords", () => { + it("", () => { + const records: Records = { + r1: { + recordId: "r1", + isEditing: false, + columns: { + c1: { + value: "grouping1", + fieldKind: "text", + }, + c2: { + value: "100", + fieldKind: "numeric", + }, + c3: { + value: "r1c3", + fieldKind: "text", + }, + }, + }, + r2: { + recordId: "r2", + isEditing: false, + columns: { + c1: { + value: "grouping1", + fieldKind: "text", + }, + c2: { + value: "200", + fieldKind: "numeric", + }, + c3: { + value: "r2c3", + fieldKind: "text", + }, + }, + }, + r3: { + recordId: "r3", + isEditing: false, + columns: { + c1: { + value: "grouping2", + fieldKind: "text", + }, + c2: { + value: "300", + fieldKind: "numeric", + }, + c3: { + value: "r3c3", + fieldKind: "text", + }, + }, + }, + }; + + const criteria: SummaryCriteria = { + groupingFields: { + c1: { + id: "c1", + fieldName: "c1", + fieldKind: "text", + fieldIndex: 1, + options: {}, + }, + }, + summaryFields: { + c2: { + id: "c2", + fieldName: "c2", + fieldKind: "numeric", + fieldIndex: 2, + options: { + thousandsSeparatorPosition: 3, + }, + }, + }, + }; + + expect(sumRecords(records, criteria)).toEqual({ + 0: { + columns: { + c1: { + value: "grouping1", + fieldKind: "text", + }, + c2: { + value: "300", + fieldKind: "numeric", + }, + }, + sum: 300, + average: 150, + count: 2, + }, + 1: { + columns: { + c1: { + value: "grouping2", + fieldKind: "text", + }, + c2: { + value: "300", + fieldKind: "numeric", + }, + }, + sum: 300, + average: 300, + count: 1, + }, + }); + }); +}); diff --git a/packages/domain/convert/summary/sumRecords.ts b/packages/domain/convert/summary/sumRecords.ts new file mode 100644 index 00000000..0a5cee73 --- /dev/null +++ b/packages/domain/convert/summary/sumRecords.ts @@ -0,0 +1,68 @@ +import { Records } from "../../schema/recordSchema"; +import { + SummaryCriteria, + SumRecords, +} from "../../schema/summary/sumRecordsSchema"; + +export const sumRecords = ( + records: Records, + criteria: SummaryCriteria, +): SumRecords => { + if ( + Object.keys(criteria.groupingFields).length < 1 || + Object.keys(criteria.summaryFields).length < 1 + ) { + throw new Error("groupingFields is empty"); + } + + const groupingFieldId = Object.values(criteria.groupingFields)[0]!.id; + const sumFieldId = Object.values(criteria.summaryFields)[0]!.id; + + const uniqueGroupingValues = [ + ...new Set( + Object.values(records).map((r) => r.columns[groupingFieldId]?.value), + ), + ]; + + const result = uniqueGroupingValues + .map((groupingValue) => ({ + groupingFieldId, + groupingValue, + sumFieldId, + })) + .flatMap((field) => { + const motherRecords = Object.values(records).filter( + (record) => + record.columns[field.groupingFieldId]?.value === field.groupingValue, + ); + + const sum = motherRecords.reduce( + (acc, record) => acc + Number(record.columns[field.sumFieldId]?.value), + 0, + ); + + return [ + { + columns: { + [field.groupingFieldId]: { + value: field.groupingValue ?? "", + fieldKind: + criteria.groupingFields[field.groupingFieldId]?.fieldKind ?? + "text", + }, + [field.sumFieldId]: { + value: sum.toString(), + fieldKind: + criteria.summaryFields[field.sumFieldId]?.fieldKind ?? + "numeric", + }, + }, + sum, + count: motherRecords.length, + average: sum / motherRecords.length, + }, + ]; + }); + + return Object.fromEntries(result.map((res, index) => [index, res])); +}; diff --git a/packages/domain/schema/summary/sumRecordsSchema.ts b/packages/domain/schema/summary/sumRecordsSchema.ts new file mode 100644 index 00000000..4e68422e --- /dev/null +++ b/packages/domain/schema/summary/sumRecordsSchema.ts @@ -0,0 +1,23 @@ +import { z } from "zod"; + +import { fieldsSchema } from "../appSchema"; +import { recordColumnsSchema } from "../recordSchema"; + +const summaryCriteriaSchema = z.object({ + groupingFields: fieldsSchema, + summaryFields: fieldsSchema, +}); + +export type SummaryCriteria = z.infer; + +const sumRecordsSchema = z.record( + z.number(), // index + z.object({ + columns: recordColumnsSchema, + sum: z.number(), + average: z.number(), + count: z.number(), + }), +); + +export type SumRecords = z.infer; diff --git a/packages/domain/tsconfig.json b/packages/domain/tsconfig.json index ffe3d3df..84f14a8a 100644 --- a/packages/domain/tsconfig.json +++ b/packages/domain/tsconfig.json @@ -1,6 +1,8 @@ { "extends": "@acme/tsconfig/base.json", "compilerOptions": { + "target": "es2015", + "downlevelIteration": true, "tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json" }, "exclude": ["node_modules"] diff --git a/tooling/github/setup/action.yml b/tooling/github/setup/action.yml index 5685a1d8..6e29909b 100644 --- a/tooling/github/setup/action.yml +++ b/tooling/github/setup/action.yml @@ -4,7 +4,7 @@ description: "Common setup steps for Actions" runs: using: composite steps: - - uses: pnpm/action-setup@v2 + - uses: pnpm/action-setup@v4 - uses: actions/setup-node@v4 with: node-version: 20