diff --git a/packages/app/src/App.css b/packages/app/src/App.css index f1b3d9049..12084a635 100644 --- a/packages/app/src/App.css +++ b/packages/app/src/App.css @@ -1,5 +1,4 @@ - .main-container { display: flex; flex-direction: column; @@ -561,3 +560,6 @@ td.extra-table-cell > svg { text-align: justify; } +.jsx-3375842594 { + height: 100% !important; +} diff --git a/packages/app/src/modules/ScorecardManagement/components/DataConfiguration/DataGroupArea.tsx b/packages/app/src/modules/ScorecardManagement/components/DataConfiguration/DataGroupArea.tsx index 93b69b6ba..27864073f 100644 --- a/packages/app/src/modules/ScorecardManagement/components/DataConfiguration/DataGroupArea.tsx +++ b/packages/app/src/modules/ScorecardManagement/components/DataConfiguration/DataGroupArea.tsx @@ -14,7 +14,7 @@ export default function DataGroupArea() {

{i18n.t("Groups")}

-
+
diff --git a/packages/app/src/modules/ScorecardManagement/components/DataConfiguration/index.tsx b/packages/app/src/modules/ScorecardManagement/components/DataConfiguration/index.tsx index 34e12f952..fa55cc997 100644 --- a/packages/app/src/modules/ScorecardManagement/components/DataConfiguration/index.tsx +++ b/packages/app/src/modules/ScorecardManagement/components/DataConfiguration/index.tsx @@ -4,21 +4,30 @@ import DataGroupArea from "./DataGroupArea"; import { PreviewArea } from "./components/DataGroups/components/DataGroup/components/PreviewArea"; import { InstructionArea } from "./components/DataGroups/components/DataGroup/components/InstructionArea"; import { SelectedDataStateProvider } from "./states/selectionState"; +import { colors, Field } from "@dhis2/ui"; +import { useController } from "react-hook-form"; +import { ScorecardConfig } from "@hisptz/dhis2-analytics"; export default function DataConfigurationScorecardForm() { + const { fieldState } = useController({ + name: "dataSelection.dataGroups" + }); + return (
-
- }> - - -
+ +
+ }> + + +
+
}> diff --git a/packages/app/src/modules/ScorecardManagement/components/General/utils/utils.ts b/packages/app/src/modules/ScorecardManagement/components/General/utils/utils.ts index 235dc5ec5..3d3224689 100644 --- a/packages/app/src/modules/ScorecardManagement/components/General/utils/utils.ts +++ b/packages/app/src/modules/ScorecardManagement/components/General/utils/utils.ts @@ -1,6 +1,7 @@ -import { generateLegendDefaults, getScorecardSummary } from "@scorecard/shared"; -import { cloneDeep, find, isEmpty, set } from "lodash"; +import { DATASTORE_NAMESPACE, generateLegendDefaults } from "@scorecard/shared"; +import { cloneDeep, isEmpty, set } from "lodash"; import { LegendDefinition, ScorecardDataGroup, ScorecardDataHolder, ScorecardDataSource, SpecificTarget } from "@hisptz/dhis2-analytics"; +import { useDataEngine } from "@dhis2/app-runtime"; export function resetLegends(groups: ScorecardDataGroup[], legendDefinitions: LegendDefinition[]) { const newGroups = cloneDeep(groups); @@ -12,23 +13,25 @@ export function resetLegends(groups: ScorecardDataGroup[], legendDefinitions: Le dataSource, "legends", generateLegendDefaults( - getNonDefaultLegendDefinitions(legendDefinitions), - dataSource.weight, - dataSource.highIsGood + { + legendDefinitions: getNonDefaultLegendDefinitions(legendDefinitions), + weight: dataSource.weight, + highIsGood: dataSource.highIsGood + } ) ); if (!isEmpty(dataSource.specificTargets)) { - dataSource.specificTargets.forEach( + dataSource.specificTargets?.forEach( (specificTarget: SpecificTarget) => { set( specificTarget, "legends", generateLegendDefaults( - getNonDefaultLegendDefinitions( - legendDefinitions - ), - dataSource.weight, - dataSource.highIsGood + { + legendDefinitions: getNonDefaultLegendDefinitions(legendDefinitions), + weight: dataSource.weight, + highIsGood: dataSource.highIsGood + } ) ); } @@ -49,14 +52,39 @@ export function getNonDefaultLegendDefinitions(legendDefinitions: any) { ); } -export async function titleDoesNotExist(engine: any, id: any, title: any) { - const { summary } = await getScorecardSummary(engine); - if (isEmpty(summary)) { + +const query: any = { + titleCheck: { + resource: `dataStore/${DATASTORE_NAMESPACE}`, + params: ({ title }: { title: string }) => ({ + fields: ["title", "id"], + filter: `title:eq:${title}` + }) + } +}; + +export async function checkTitleAvailability({ title, id, engine }: { engine: ReturnType, id?: string, title?: string }) { + if (!title) { + return false; + } + + const response = await engine.query(query, { + variables: { + title + } + }); + + const results = (response.titleCheck as { entries: Record[] })?.entries; + if (isEmpty(results)) { return true; } - const scorecard = find(summary, { title }); - if (scorecard) { - return scorecard.id === id; + if (results.length > 1) { + return false; } - return !scorecard; + if (!id) { + return true; + } + + const existingConfig = results.find((result) => result.id === id); + return !existingConfig; } diff --git a/packages/app/src/modules/ScorecardManagement/components/NavigationButtons.tsx b/packages/app/src/modules/ScorecardManagement/components/NavigationButtons.tsx index 4c5fb7c4d..ea80a63cb 100644 --- a/packages/app/src/modules/ScorecardManagement/components/NavigationButtons.tsx +++ b/packages/app/src/modules/ScorecardManagement/components/NavigationButtons.tsx @@ -51,7 +51,6 @@ export function NavigationButtons() { loading: true }); const formValid = await trigger(currentStep.fieldIds as any); - if (formValid) { const config = getValues(); await save(config); diff --git a/packages/app/src/modules/ScorecardManagement/hooks/schema.ts b/packages/app/src/modules/ScorecardManagement/hooks/schema.ts index 158b9519f..859a69435 100644 --- a/packages/app/src/modules/ScorecardManagement/hooks/schema.ts +++ b/packages/app/src/modules/ScorecardManagement/hooks/schema.ts @@ -1,7 +1,7 @@ import { scorecardConfigSchema } from "@scorecard/shared"; import { z } from "zod"; import i18n from "@dhis2/d2-i18n"; -import { titleDoesNotExist } from "../components/General/utils/utils"; +import { checkTitleAvailability } from "../components/General/utils/utils"; import { useParams } from "react-router-dom"; import { useDataEngine } from "@dhis2/app-runtime"; import { dataGroupSchema, dataHolderSchema, organisationUnitSelectionSchema } from "@hisptz/dhis2-analytics"; @@ -31,11 +31,9 @@ export function useFormSchema() { const engine = useDataEngine(); return scorecardConfigSchema.extend({ title: z.string({ required_error: i18n.t("Title is required") }).min(4, i18n.t("Title must have at least 4 characters")).refine(async (value) => { - const titleExists = await titleDoesNotExist(engine, id, value); - return !titleExists || i18n.t( - `A scorecard with the title '{{value}}' already exists. Please select another title`, - { value } - ); + return await checkTitleAvailability({ engine, id, title: value }); + }, { + message: i18n.t(`A scorecard with this title already exists. Please select another title`) }), dataSelection: z.object({ dataGroups: z.array(dataGroupSchema.extend({ diff --git a/packages/shared/src/services/getScorecardSummary.ts b/packages/shared/src/services/getScorecardSummary.ts index 58d394a38..40d616654 100644 --- a/packages/shared/src/services/getScorecardSummary.ts +++ b/packages/shared/src/services/getScorecardSummary.ts @@ -1,9 +1,7 @@ import { compact, filter, isEmpty } from "lodash"; -import { - DATASTORE_ENDPOINT, - DATASTORE_SCORECARD_SUMMARY_KEY, -} from "../constants"; +import { DATASTORE_ENDPOINT, DATASTORE_SCORECARD_SUMMARY_KEY } from "../constants"; import { generateScorecardSummary } from "../utils"; +import { useDataEngine } from "@dhis2/app-runtime"; const query = { summary: { @@ -22,7 +20,7 @@ async function initializeKey(engine: any) { return await engine.mutate(addMutation, { variables: { data: [] } }); } -export default async function getScorecardSummary(engine: any) { +export default async function getScorecardSummary(engine: ReturnType) { try { const response = await engine.query(query); return { summary: response?.summary }; @@ -47,7 +45,7 @@ const restoreMutation = { id: DATASTORE_SCORECARD_SUMMARY_KEY, data: ({ data }: any) => data, }; - +const { summary } = await getScorecardSummary(engine); const singleScorecardQuery = { scorecard: { resource: DATASTORE_ENDPOINT,