From 3f9a2914ed04489170d202ea36ef0b2e1b65dc56 Mon Sep 17 00:00:00 2001 From: Raphael Blum Date: Wed, 4 Sep 2024 10:39:56 +0200 Subject: [PATCH 1/6] add basic warning entity --- demo/admin/crud-generator-config.ts | 5 + demo/admin/src/common/MasterMenu.tsx | 10 + .../src/warnings/generated/WarningForm.gql.ts | 53 +++++ .../src/warnings/generated/WarningForm.tsx | 182 ++++++++++++++++ .../src/warnings/generated/WarningsGrid.tsx | 206 ++++++++++++++++++ .../src/warnings/generated/WarningsPage.tsx | 30 +++ demo/api/schema.gql | 78 +++++++ demo/api/src/app.module.ts | 2 + .../db/migrations/Migration20240725161414.ts | 13 ++ .../warnings/entities/warning-level.enum.ts | 10 + .../warnings/entities/warning-state.enum.ts | 10 + .../src/warnings/entities/warning.entity.ts | 43 ++++ .../generated/dto/paginated-warnings.ts | 9 + .../warnings/generated/dto/warning.filter.ts | 59 +++++ .../warnings/generated/dto/warning.input.ts | 29 +++ .../warnings/generated/dto/warning.sort.ts | 27 +++ .../warnings/generated/dto/warnings.args.ts | 29 +++ .../warnings/generated/warning.resolver.ts | 80 +++++++ demo/api/src/warnings/warning.module.ts | 11 + 19 files changed, 886 insertions(+) create mode 100644 demo/admin/src/warnings/generated/WarningForm.gql.ts create mode 100644 demo/admin/src/warnings/generated/WarningForm.tsx create mode 100644 demo/admin/src/warnings/generated/WarningsGrid.tsx create mode 100644 demo/admin/src/warnings/generated/WarningsPage.tsx create mode 100644 demo/api/src/db/migrations/Migration20240725161414.ts create mode 100644 demo/api/src/warnings/entities/warning-level.enum.ts create mode 100644 demo/api/src/warnings/entities/warning-state.enum.ts create mode 100644 demo/api/src/warnings/entities/warning.entity.ts create mode 100644 demo/api/src/warnings/generated/dto/paginated-warnings.ts create mode 100644 demo/api/src/warnings/generated/dto/warning.filter.ts create mode 100644 demo/api/src/warnings/generated/dto/warning.input.ts create mode 100644 demo/api/src/warnings/generated/dto/warning.sort.ts create mode 100644 demo/api/src/warnings/generated/dto/warnings.args.ts create mode 100644 demo/api/src/warnings/generated/warning.resolver.ts create mode 100644 demo/api/src/warnings/warning.module.ts diff --git a/demo/admin/crud-generator-config.ts b/demo/admin/crud-generator-config.ts index 2b2b8f912f..ff6b3b7dbd 100644 --- a/demo/admin/crud-generator-config.ts +++ b/demo/admin/crud-generator-config.ts @@ -1,4 +1,5 @@ import { CrudGeneratorConfig } from "@comet/cms-admin"; + export default [ { target: "src/products/generated", @@ -8,4 +9,8 @@ export default [ target: "src/news/generated", entityName: "News", }, + { + target: "src/warnings/generated", + entityName: "Warning", + }, ] satisfies CrudGeneratorConfig[]; diff --git a/demo/admin/src/common/MasterMenu.tsx b/demo/admin/src/common/MasterMenu.tsx index 3f6b15b1a7..9905291e59 100644 --- a/demo/admin/src/common/MasterMenu.tsx +++ b/demo/admin/src/common/MasterMenu.tsx @@ -32,6 +32,7 @@ import { ProductsPage } from "@src/products/generated/ProductsPage"; import { ManufacturersPage as ManufacturersHandmadePage } from "@src/products/ManufacturersPage"; import ProductsHandmadePage from "@src/products/ProductsPage"; import ProductTagsPage from "@src/products/tags/ProductTagsPage"; +import { WarningsPage } from "@src/warnings/generated/WarningsPage"; import { FormattedMessage } from "react-intl"; import { Redirect, RouteComponentProps } from "react-router-dom"; @@ -175,6 +176,15 @@ export const masterMenuData: MasterMenuData = [ }, requiredPermission: "pageTree", }, + { + type: "route", + primary: , + route: { + path: "/system/warnings", + component: WarningsPage, + }, + requiredPermission: "pageTree", + }, ], requiredPermission: "pageTree", }, diff --git a/demo/admin/src/warnings/generated/WarningForm.gql.ts b/demo/admin/src/warnings/generated/WarningForm.gql.ts new file mode 100644 index 0000000000..4faa2364ef --- /dev/null +++ b/demo/admin/src/warnings/generated/WarningForm.gql.ts @@ -0,0 +1,53 @@ +// This file has been generated by comet admin-generator. +// You may choose to use this file as scaffold by moving this file out of generated folder and removing this comment. + +import { gql } from "@apollo/client"; + +export const warningFormFragment = gql` + fragment WarningForm on Warning { + type + level + state + } +`; + +export const warningFormQuery = gql` + query WarningForm($id: ID!) { + warning(id: $id) { + id + updatedAt + ...WarningForm + } + } + ${warningFormFragment} +`; + +export const warningFormCheckForChangesQuery = gql` + query WarningFormCheckForChanges($id: ID!) { + warning(id: $id) { + updatedAt + } + } +`; + +export const createWarningMutation = gql` + mutation CreateWarning($input: WarningInput!) { + createWarning(input: $input) { + id + updatedAt + ...WarningForm + } + } + ${warningFormFragment} +`; + +export const updateWarningMutation = gql` + mutation UpdateWarning($id: ID!, $input: WarningUpdateInput!) { + updateWarning(id: $id, input: $input) { + id + updatedAt + ...WarningForm + } + } + ${warningFormFragment} +`; diff --git a/demo/admin/src/warnings/generated/WarningForm.tsx b/demo/admin/src/warnings/generated/WarningForm.tsx new file mode 100644 index 0000000000..1b031ed650 --- /dev/null +++ b/demo/admin/src/warnings/generated/WarningForm.tsx @@ -0,0 +1,182 @@ +// This file has been generated by comet admin-generator. +// You may choose to use this file as scaffold by moving this file out of generated folder and removing this comment. + +import { useApolloClient, useQuery } from "@apollo/client"; +import { + Field, + filterByFragment, + FinalForm, + FinalFormSaveButton, + FinalFormSelect, + FinalFormSubmitEvent, + Loading, + MainContent, + TextField, + Toolbar, + ToolbarActions, + ToolbarFillSpace, + ToolbarItem, + ToolbarTitleItem, + useFormApiRef, + useStackApi, + useStackSwitchApi, +} from "@comet/admin"; +import { ArrowLeft } from "@comet/admin-icons"; +import { ContentScopeIndicator, queryUpdatedAt, resolveHasSaveConflict, useFormSaveConflict } from "@comet/cms-admin"; +import { IconButton, MenuItem } from "@mui/material"; +import { FormApi } from "final-form"; +import React from "react"; +import { FormattedMessage } from "react-intl"; + +import { createWarningMutation, updateWarningMutation, warningFormFragment, warningFormQuery } from "./WarningForm.gql"; +import { + GQLCreateWarningMutation, + GQLCreateWarningMutationVariables, + GQLUpdateWarningMutation, + GQLUpdateWarningMutationVariables, + GQLWarningFormFragment, + GQLWarningFormQuery, + GQLWarningFormQueryVariables, +} from "./WarningForm.gql.generated"; + +type FormValues = GQLWarningFormFragment; + +interface FormProps { + id?: string; +} + +export function WarningForm({ id }: FormProps): React.ReactElement { + const stackApi = useStackApi(); + const client = useApolloClient(); + const mode = id ? "edit" : "add"; + const formApiRef = useFormApiRef(); + const stackSwitchApi = useStackSwitchApi(); + + const { data, error, loading, refetch } = useQuery( + warningFormQuery, + id ? { variables: { id } } : { skip: true }, + ); + + const initialValues = React.useMemo>( + () => + data?.warning + ? { + ...filterByFragment(warningFormFragment, data.warning), + } + : {}, + [data], + ); + + const saveConflict = useFormSaveConflict({ + checkConflict: async () => { + const updatedAt = await queryUpdatedAt(client, "warning", id); + return resolveHasSaveConflict(data?.warning.updatedAt, updatedAt); + }, + formApiRef, + loadLatestVersion: async () => { + await refetch(); + }, + }); + + const handleSubmit = async (state: FormValues, form: FormApi, event: FinalFormSubmitEvent) => { + if (await saveConflict.checkForConflicts()) { + throw new Error("Conflicts detected"); + } + + const output = { + ...state, + }; + + if (mode === "edit") { + if (!id) { + throw new Error("Missing id in edit mode"); + } + await client.mutate({ + mutation: updateWarningMutation, + variables: { id, input: output }, + }); + } else { + const { data: mutationResponse } = await client.mutate({ + mutation: createWarningMutation, + variables: { input: output }, + }); + if (!event.navigatingBack) { + const id = mutationResponse?.createWarning.id; + if (id) { + setTimeout(() => { + stackSwitchApi.activatePage("edit", id); + }); + } + } + } + }; + + if (error) throw error; + + if (loading) { + return ; + } + + return ( + apiRef={formApiRef} onSubmit={handleSubmit} mode={mode} initialValues={initialValues}> + {({ values }) => ( + <> + {saveConflict.dialogs} + }> + + + + + + + + + + + + + + + } + /> + }> + {(props) => ( + + + + + + + + + + + + )} + + }> + {(props) => ( + + + + + + + + + + + + )} + + + + )} + + ); +} diff --git a/demo/admin/src/warnings/generated/WarningsGrid.tsx b/demo/admin/src/warnings/generated/WarningsGrid.tsx new file mode 100644 index 0000000000..b5f7552fdb --- /dev/null +++ b/demo/admin/src/warnings/generated/WarningsGrid.tsx @@ -0,0 +1,206 @@ +// This file has been generated by comet admin-generator. +// You may choose to use this file as scaffold by moving this file out of generated folder and removing this comment. +import { gql, useApolloClient, useQuery } from "@apollo/client"; +import { + CrudContextMenu, + DataGridToolbar, + GridColDef, + GridFilterButton, + MainContent, + muiGridFilterToGql, + muiGridSortToGql, + StackLink, + ToolbarActions, + ToolbarFillSpace, + ToolbarItem, + useBufferedRowCount, + useDataGridRemote, + usePersistentColumnState, +} from "@comet/admin"; +import { Add as AddIcon, Edit } from "@comet/admin-icons"; +import { Button, IconButton } from "@mui/material"; +import { DataGridPro, GridToolbarQuickFilter } from "@mui/x-data-grid-pro"; +import * as React from "react"; +import { FormattedMessage, useIntl } from "react-intl"; + +import { + GQLCreateWarningMutation, + GQLCreateWarningMutationVariables, + GQLDeleteWarningMutation, + GQLDeleteWarningMutationVariables, + GQLWarningsGridQuery, + GQLWarningsGridQueryVariables, + GQLWarningsListFragment, +} from "./WarningsGrid.generated"; + +const warningsFragment = gql` + fragment WarningsList on Warning { + id + createdAt + updatedAt + type + level + state + } +`; + +const warningsQuery = gql` + query WarningsGrid($offset: Int, $limit: Int, $sort: [WarningSort!], $search: String, $filter: WarningFilter) { + warnings(offset: $offset, limit: $limit, sort: $sort, search: $search, filter: $filter) { + nodes { + ...WarningsList + } + totalCount + } + } + ${warningsFragment} +`; + +const deleteWarningMutation = gql` + mutation DeleteWarning($id: ID!) { + deleteWarning(id: $id) + } +`; + +const createWarningMutation = gql` + mutation CreateWarning($input: WarningInput!) { + createWarning(input: $input) { + id + } + } +`; + +function WarningsGridToolbar() { + return ( + + + + + + + + + + + + + ); +} + +export function WarningsGrid(): React.ReactElement { + const client = useApolloClient(); + const intl = useIntl(); + const dataGridProps = { ...useDataGridRemote(), ...usePersistentColumnState("WarningsGrid") }; + + const columns: GridColDef[] = [ + { + field: "createdAt", + headerName: intl.formatMessage({ id: "warning.createdAt", defaultMessage: "Created At" }), + type: "dateTime", + valueGetter: ({ value }) => value && new Date(value), + width: 150, + }, + { + field: "updatedAt", + headerName: intl.formatMessage({ id: "warning.updatedAt", defaultMessage: "Updated At" }), + type: "dateTime", + valueGetter: ({ value }) => value && new Date(value), + width: 150, + }, + { field: "type", headerName: intl.formatMessage({ id: "warning.type", defaultMessage: "Type" }), width: 150 }, + { + field: "level", + headerName: intl.formatMessage({ id: "warning.level", defaultMessage: "Level" }), + type: "singleSelect", + valueOptions: [ + { value: "critical", label: intl.formatMessage({ id: "warning.level.critical", defaultMessage: "Critical" }) }, + { value: "high", label: intl.formatMessage({ id: "warning.level.high", defaultMessage: "High" }) }, + { value: "low", label: intl.formatMessage({ id: "warning.level.low", defaultMessage: "Low" }) }, + ], + width: 150, + }, + { + field: "state", + headerName: intl.formatMessage({ id: "warning.state", defaultMessage: "State" }), + type: "singleSelect", + valueOptions: [ + { value: "open", label: intl.formatMessage({ id: "warning.state.open", defaultMessage: "Open" }) }, + { value: "resolved", label: intl.formatMessage({ id: "warning.state.resolved", defaultMessage: "Resolved" }) }, + { value: "ignored", label: intl.formatMessage({ id: "warning.state.ignored", defaultMessage: "Ignored" }) }, + ], + width: 150, + }, + { + field: "actions", + headerName: "", + sortable: false, + filterable: false, + type: "actions", + renderCell: (params) => { + return ( + <> + + + + { + const row = params.row; + return { + type: row.type, + level: row.level, + state: row.state, + }; + }} + onPaste={async ({ input }) => { + await client.mutate({ + mutation: createWarningMutation, + variables: { input }, + }); + }} + onDelete={async () => { + await client.mutate({ + mutation: deleteWarningMutation, + variables: { id: params.row.id }, + }); + }} + refetchQueries={[warningsQuery]} + /> + + ); + }, + }, + ]; + + const { filter: gqlFilter, search: gqlSearch } = muiGridFilterToGql(columns, dataGridProps.filterModel); + + const { data, loading, error } = useQuery(warningsQuery, { + variables: { + filter: gqlFilter, + search: gqlSearch, + offset: dataGridProps.page * dataGridProps.pageSize, + limit: dataGridProps.pageSize, + sort: muiGridSortToGql(dataGridProps.sortModel), + }, + }); + const rowCount = useBufferedRowCount(data?.warnings.totalCount); + if (error) throw error; + const rows = data?.warnings.nodes ?? []; + + return ( + + + + ); +} diff --git a/demo/admin/src/warnings/generated/WarningsPage.tsx b/demo/admin/src/warnings/generated/WarningsPage.tsx new file mode 100644 index 0000000000..d639b8e19b --- /dev/null +++ b/demo/admin/src/warnings/generated/WarningsPage.tsx @@ -0,0 +1,30 @@ +// This file has been generated by comet admin-generator. +// You may choose to use this file as scaffold by moving this file out of generated folder and removing this comment. + +import { Stack, StackPage, StackSwitch, StackToolbar } from "@comet/admin"; +import { ContentScopeIndicator } from "@comet/cms-admin"; +import * as React from "react"; +import { useIntl } from "react-intl"; + +import { WarningForm } from "./WarningForm"; +import { WarningsGrid } from "./WarningsGrid"; + +export function WarningsPage(): React.ReactElement { + const intl = useIntl(); + return ( + + + + } /> + + + + {(selectedId) => } + + + + + + + ); +} diff --git a/demo/api/schema.gql b/demo/api/schema.gql index a87b1f2453..5cf2ece4ce 100644 --- a/demo/api/schema.gql +++ b/demo/api/schema.gql @@ -605,6 +605,32 @@ type RedirectScope { domain: String! } +type Warning { + id: ID! + createdAt: DateTime! + updatedAt: DateTime! + type: String! + level: WarningLevel! + state: WarningState! +} + +enum WarningLevel { + critical + high + low +} + +enum WarningState { + open + resolved + ignored +} + +type PaginatedWarnings { + nodes: [Warning!]! + totalCount: Int! +} + type PaginatedPageTreeNodes { nodes: [PageTreeNode!]! totalCount: Int! @@ -790,6 +816,8 @@ type Query { manufacturers(offset: Int! = 0, limit: Int! = 25, search: String, filter: ManufacturerFilter, sort: [ManufacturerSort!]): PaginatedManufacturers! manufacturerCountry(id: ID!): ManufacturerCountry! manufacturerCountries(offset: Int! = 0, limit: Int! = 25, search: String, filter: ManufacturerCountryFilter): PaginatedManufacturerCountries! + warning(id: ID!): Warning! + warnings(offset: Int! = 0, limit: Int! = 25, search: String, filter: WarningFilter, sort: [WarningSort!]): PaginatedWarnings! } input UserFilter { @@ -1149,6 +1177,41 @@ input ManufacturerCountryFilter { or: [ManufacturerCountryFilter!] } +input WarningFilter { + createdAt: DateTimeFilter + updatedAt: DateTimeFilter + type: StringFilter + level: WarningLevelEnumFilter + state: WarningStateEnumFilter + and: [WarningFilter!] + or: [WarningFilter!] +} + +input WarningLevelEnumFilter { + isAnyOf: [WarningLevel!] + equal: WarningLevel + notEqual: WarningLevel +} + +input WarningStateEnumFilter { + isAnyOf: [WarningState!] + equal: WarningState + notEqual: WarningState +} + +input WarningSort { + field: WarningSortField! + direction: SortDirection! = ASC +} + +enum WarningSortField { + createdAt + updatedAt + type + level + state +} + type Mutation { currentUserSignOut: String! userPermissionsCreatePermission(userId: String!, input: UserPermissionInput!): UserPermission! @@ -1209,6 +1272,9 @@ type Mutation { createManufacturer(input: ManufacturerInput!): Manufacturer! updateManufacturer(id: ID!, input: ManufacturerUpdateInput!): Manufacturer! deleteManufacturer(id: ID!): Boolean! + createWarning(input: WarningInput!): Warning! + updateWarning(id: ID!, input: WarningUpdateInput!): Warning! + deleteWarning(id: ID!): Boolean! } input UserPermissionInput { @@ -1498,3 +1564,15 @@ input ManufacturerUpdateInput { address: AddressInput addressAsEmbeddable: AddressAsEmbeddableInput } + +input WarningInput { + type: String! + level: WarningLevel! + state: WarningState! +} + +input WarningUpdateInput { + type: String + level: WarningLevel + state: WarningState +} diff --git a/demo/api/src/app.module.ts b/demo/api/src/app.module.ts index 2973d172a5..949c0df48e 100644 --- a/demo/api/src/app.module.ts +++ b/demo/api/src/app.module.ts @@ -49,6 +49,7 @@ import { Page } from "./pages/entities/page.entity"; import { PredefinedPageModule } from "./predefined-page/predefined-page.module"; import { ProductsModule } from "./products/products.module"; import { RedirectScope } from "./redirects/dto/redirect-scope"; +import { WarningsModule } from "./warnings/warning.module"; @Module({}) export class AppModule { @@ -170,6 +171,7 @@ export class AppModule { }, }), ...(config.sentry ? [SentryModule.forRootAsync(config.sentry)] : []), + WarningsModule, ], }; } diff --git a/demo/api/src/db/migrations/Migration20240725161414.ts b/demo/api/src/db/migrations/Migration20240725161414.ts new file mode 100644 index 0000000000..ecf18e67b8 --- /dev/null +++ b/demo/api/src/db/migrations/Migration20240725161414.ts @@ -0,0 +1,13 @@ +import { Migration } from '@mikro-orm/migrations'; + +export class Migration20240725161414 extends Migration { + + async up(): Promise { + this.addSql('create table "Warning" ("id" uuid not null, "createdAt" timestamptz(0) not null, "updatedAt" timestamptz(0) not null, "type" varchar(255) not null, "level" text check ("level" in (\'critical\', \'high\', \'low\')) not null, "state" text check ("state" in (\'open\', \'resolved\', \'ignored\')) not null, constraint "Warning_pkey" primary key ("id"));'); + } + + async down(): Promise { + this.addSql('drop table if exists "Warning" cascade;'); + } + +} diff --git a/demo/api/src/warnings/entities/warning-level.enum.ts b/demo/api/src/warnings/entities/warning-level.enum.ts new file mode 100644 index 0000000000..1b71249125 --- /dev/null +++ b/demo/api/src/warnings/entities/warning-level.enum.ts @@ -0,0 +1,10 @@ +import { registerEnumType } from "@nestjs/graphql"; + +export enum WarningLevel { + critical = "critical", + high = "high", + low = "low", +} +registerEnumType(WarningLevel, { + name: "WarningLevel", +}); diff --git a/demo/api/src/warnings/entities/warning-state.enum.ts b/demo/api/src/warnings/entities/warning-state.enum.ts new file mode 100644 index 0000000000..f54cad8b37 --- /dev/null +++ b/demo/api/src/warnings/entities/warning-state.enum.ts @@ -0,0 +1,10 @@ +import { registerEnumType } from "@nestjs/graphql"; + +export enum WarningState { + open = "open", + resolved = "resolved", + ignored = "ignored", +} +registerEnumType(WarningState, { + name: "WarningState", +}); diff --git a/demo/api/src/warnings/entities/warning.entity.ts b/demo/api/src/warnings/entities/warning.entity.ts new file mode 100644 index 0000000000..68265aaf87 --- /dev/null +++ b/demo/api/src/warnings/entities/warning.entity.ts @@ -0,0 +1,43 @@ +import { RootBlockEntity } from "@comet/blocks-api"; +import { CrudField, CrudGenerator } from "@comet/cms-api"; +import { BaseEntity, Entity, Enum, OptionalProps, PrimaryKey, Property } from "@mikro-orm/core"; +import { Field, ID, ObjectType } from "@nestjs/graphql"; +import { v4 as uuid } from "uuid"; + +import { WarningLevel } from "./warning-level.enum"; +import { WarningState } from "./warning-state.enum"; + +@ObjectType() +@Entity() +@RootBlockEntity() +@CrudGenerator({ targetDirectory: `${__dirname}/../generated/`, requiredPermission: ["pageTree"] }) +export class Warning extends BaseEntity { + [OptionalProps]?: "createdAt" | "updatedAt" | "status"; + + @PrimaryKey({ type: "uuid" }) + @Field(() => ID) + id: string = uuid(); + + @Property() + @Field() + createdAt: Date = new Date(); + + @Property({ onUpdate: () => new Date() }) + @Field() + updatedAt: Date = new Date(); + + @Property() + @Field() + @CrudField() + type: string; + + @Enum({ items: () => WarningLevel }) + @Field(() => WarningLevel) + level: WarningLevel; + + // TODO: add blockInfos with COM-958 + + @Enum({ items: () => WarningState }) + @Field(() => WarningState) + state: WarningState; +} diff --git a/demo/api/src/warnings/generated/dto/paginated-warnings.ts b/demo/api/src/warnings/generated/dto/paginated-warnings.ts new file mode 100644 index 0000000000..7c2cf70c67 --- /dev/null +++ b/demo/api/src/warnings/generated/dto/paginated-warnings.ts @@ -0,0 +1,9 @@ +// This file has been generated by comet api-generator. +// You may choose to use this file as scaffold by moving this file out of generated folder and removing this comment. +import { PaginatedResponseFactory } from "@comet/cms-api"; +import { ObjectType } from "@nestjs/graphql"; + +import { Warning } from "../../entities/warning.entity"; + +@ObjectType() +export class PaginatedWarnings extends PaginatedResponseFactory.create(Warning) {} diff --git a/demo/api/src/warnings/generated/dto/warning.filter.ts b/demo/api/src/warnings/generated/dto/warning.filter.ts new file mode 100644 index 0000000000..038aca5b58 --- /dev/null +++ b/demo/api/src/warnings/generated/dto/warning.filter.ts @@ -0,0 +1,59 @@ +// This file has been generated by comet api-generator. +// You may choose to use this file as scaffold by moving this file out of generated folder and removing this comment. +import { createEnumFilter, DateTimeFilter, StringFilter } from "@comet/cms-api"; +import { Field, InputType } from "@nestjs/graphql"; +import { Type } from "class-transformer"; +import { IsOptional, ValidateNested } from "class-validator"; + +import { WarningLevel } from "../../entities/warning-level.enum"; +import { WarningState } from "../../entities/warning-state.enum"; + +@InputType() +class WarningLevelEnumFilter extends createEnumFilter(WarningLevel) {} +@InputType() +class WarningStateEnumFilter extends createEnumFilter(WarningState) {} + +@InputType() +export class WarningFilter { + @Field(() => DateTimeFilter, { nullable: true }) + @ValidateNested() + @IsOptional() + @Type(() => DateTimeFilter) + createdAt?: DateTimeFilter; + + @Field(() => DateTimeFilter, { nullable: true }) + @ValidateNested() + @IsOptional() + @Type(() => DateTimeFilter) + updatedAt?: DateTimeFilter; + + @Field(() => StringFilter, { nullable: true }) + @ValidateNested() + @IsOptional() + @Type(() => StringFilter) + type?: StringFilter; + + @Field(() => WarningLevelEnumFilter, { nullable: true }) + @ValidateNested() + @IsOptional() + @Type(() => WarningLevelEnumFilter) + level?: WarningLevelEnumFilter; + + @Field(() => WarningStateEnumFilter, { nullable: true }) + @ValidateNested() + @IsOptional() + @Type(() => WarningStateEnumFilter) + state?: WarningStateEnumFilter; + + @Field(() => [WarningFilter], { nullable: true }) + @Type(() => WarningFilter) + @ValidateNested({ each: true }) + @IsOptional() + and?: WarningFilter[]; + + @Field(() => [WarningFilter], { nullable: true }) + @Type(() => WarningFilter) + @ValidateNested({ each: true }) + @IsOptional() + or?: WarningFilter[]; +} diff --git a/demo/api/src/warnings/generated/dto/warning.input.ts b/demo/api/src/warnings/generated/dto/warning.input.ts new file mode 100644 index 0000000000..16e7050149 --- /dev/null +++ b/demo/api/src/warnings/generated/dto/warning.input.ts @@ -0,0 +1,29 @@ +// This file has been generated by comet api-generator. +// You may choose to use this file as scaffold by moving this file out of generated folder and removing this comment. +import { PartialType } from "@comet/cms-api"; +import { Field, InputType } from "@nestjs/graphql"; +import { IsEnum, IsNotEmpty, IsString } from "class-validator"; + +import { WarningLevel } from "../../entities/warning-level.enum"; +import { WarningState } from "../../entities/warning-state.enum"; + +@InputType() +export class WarningInput { + @IsNotEmpty() + @IsString() + @Field() + type: string; + + @IsNotEmpty() + @IsEnum(WarningLevel) + @Field(() => WarningLevel) + level: WarningLevel; + + @IsNotEmpty() + @IsEnum(WarningState) + @Field(() => WarningState) + state: WarningState; +} + +@InputType() +export class WarningUpdateInput extends PartialType(WarningInput) {} diff --git a/demo/api/src/warnings/generated/dto/warning.sort.ts b/demo/api/src/warnings/generated/dto/warning.sort.ts new file mode 100644 index 0000000000..c68e246aad --- /dev/null +++ b/demo/api/src/warnings/generated/dto/warning.sort.ts @@ -0,0 +1,27 @@ +// This file has been generated by comet api-generator. +// You may choose to use this file as scaffold by moving this file out of generated folder and removing this comment. +import { SortDirection } from "@comet/cms-api"; +import { Field, InputType, registerEnumType } from "@nestjs/graphql"; +import { IsEnum } from "class-validator"; + +export enum WarningSortField { + createdAt = "createdAt", + updatedAt = "updatedAt", + type = "type", + level = "level", + state = "state", +} +registerEnumType(WarningSortField, { + name: "WarningSortField", +}); + +@InputType() +export class WarningSort { + @Field(() => WarningSortField) + @IsEnum(WarningSortField) + field: WarningSortField; + + @Field(() => SortDirection, { defaultValue: SortDirection.ASC }) + @IsEnum(SortDirection) + direction: SortDirection = SortDirection.ASC; +} diff --git a/demo/api/src/warnings/generated/dto/warnings.args.ts b/demo/api/src/warnings/generated/dto/warnings.args.ts new file mode 100644 index 0000000000..9e283c274f --- /dev/null +++ b/demo/api/src/warnings/generated/dto/warnings.args.ts @@ -0,0 +1,29 @@ +// This file has been generated by comet api-generator. +// You may choose to use this file as scaffold by moving this file out of generated folder and removing this comment. +import { OffsetBasedPaginationArgs } from "@comet/cms-api"; +import { ArgsType, Field } from "@nestjs/graphql"; +import { Type } from "class-transformer"; +import { IsOptional, IsString, ValidateNested } from "class-validator"; + +import { WarningFilter } from "./warning.filter"; +import { WarningSort } from "./warning.sort"; + +@ArgsType() +export class WarningsArgs extends OffsetBasedPaginationArgs { + @Field({ nullable: true }) + @IsOptional() + @IsString() + search?: string; + + @Field(() => WarningFilter, { nullable: true }) + @ValidateNested() + @Type(() => WarningFilter) + @IsOptional() + filter?: WarningFilter; + + @Field(() => [WarningSort], { nullable: true }) + @ValidateNested({ each: true }) + @Type(() => WarningSort) + @IsOptional() + sort?: WarningSort[]; +} diff --git a/demo/api/src/warnings/generated/warning.resolver.ts b/demo/api/src/warnings/generated/warning.resolver.ts new file mode 100644 index 0000000000..ec3c11e7ba --- /dev/null +++ b/demo/api/src/warnings/generated/warning.resolver.ts @@ -0,0 +1,80 @@ +// This file has been generated by comet api-generator. +// You may choose to use this file as scaffold by moving this file out of generated folder and removing this comment. +import { AffectedEntity, gqlArgsToMikroOrmQuery, RequiredPermission } from "@comet/cms-api"; +import { FindOptions } from "@mikro-orm/core"; +import { InjectRepository } from "@mikro-orm/nestjs"; +import { EntityManager, EntityRepository } from "@mikro-orm/postgresql"; +import { Args, ID, Mutation, Query, Resolver } from "@nestjs/graphql"; + +import { Warning } from "../entities/warning.entity"; +import { PaginatedWarnings } from "./dto/paginated-warnings"; +import { WarningInput, WarningUpdateInput } from "./dto/warning.input"; +import { WarningsArgs } from "./dto/warnings.args"; + +@Resolver(() => Warning) +@RequiredPermission(["pageTree"], { skipScopeCheck: true }) +export class WarningResolver { + constructor(private readonly entityManager: EntityManager, @InjectRepository(Warning) private readonly repository: EntityRepository) {} + + @Query(() => Warning) + @AffectedEntity(Warning) + async warning(@Args("id", { type: () => ID }) id: string): Promise { + const warning = await this.repository.findOneOrFail(id); + return warning; + } + + @Query(() => PaginatedWarnings) + async warnings(@Args() { search, filter, sort, offset, limit }: WarningsArgs): Promise { + const where = gqlArgsToMikroOrmQuery({ search, filter }, this.repository); + + const options: FindOptions = { offset, limit }; + + if (sort) { + options.orderBy = sort.map((sortItem) => { + return { + [sortItem.field]: sortItem.direction, + }; + }); + } + + const [entities, totalCount] = await this.repository.findAndCount(where, options); + return new PaginatedWarnings(entities, totalCount); + } + + @Mutation(() => Warning) + async createWarning(@Args("input", { type: () => WarningInput }) input: WarningInput): Promise { + const warning = this.repository.create({ + ...input, + }); + + await this.entityManager.flush(); + + return warning; + } + + @Mutation(() => Warning) + @AffectedEntity(Warning) + async updateWarning( + @Args("id", { type: () => ID }) id: string, + @Args("input", { type: () => WarningUpdateInput }) input: WarningUpdateInput, + ): Promise { + const warning = await this.repository.findOneOrFail(id); + + warning.assign({ + ...input, + }); + + await this.entityManager.flush(); + + return warning; + } + + @Mutation(() => Boolean) + @AffectedEntity(Warning) + async deleteWarning(@Args("id", { type: () => ID }) id: string): Promise { + const warning = await this.repository.findOneOrFail(id); + this.entityManager.remove(warning); + await this.entityManager.flush(); + return true; + } +} diff --git a/demo/api/src/warnings/warning.module.ts b/demo/api/src/warnings/warning.module.ts new file mode 100644 index 0000000000..3be79caff9 --- /dev/null +++ b/demo/api/src/warnings/warning.module.ts @@ -0,0 +1,11 @@ +import { MikroOrmModule } from "@mikro-orm/nestjs"; +import { Module } from "@nestjs/common"; + +import { Warning } from "./entities/warning.entity"; +import { WarningResolver } from "./generated/warning.resolver"; + +@Module({ + imports: [MikroOrmModule.forFeature([Warning])], + providers: [WarningResolver], +}) +export class WarningsModule {} From ea234a7c877e8ef52d461b573b8e96f5e683ec98 Mon Sep 17 00:00:00 2001 From: Raphael Blum Date: Fri, 4 Oct 2024 11:36:14 +0200 Subject: [PATCH 2/6] scaffold admin generator code and adapt (remove unnecessary code and style grid columns) --- demo/admin/src/common/MasterMenu.tsx | 2 +- demo/admin/src/warnings/WarningsGrid.tsx | 152 +++++++++++++ demo/admin/src/warnings/WarningsPage.tsx | 14 ++ .../src/warnings/generated/WarningForm.gql.ts | 53 ----- .../src/warnings/generated/WarningForm.tsx | 182 ---------------- .../src/warnings/generated/WarningsGrid.tsx | 206 ------------------ .../src/warnings/generated/WarningsPage.tsx | 30 --- 7 files changed, 167 insertions(+), 472 deletions(-) create mode 100644 demo/admin/src/warnings/WarningsGrid.tsx create mode 100644 demo/admin/src/warnings/WarningsPage.tsx delete mode 100644 demo/admin/src/warnings/generated/WarningForm.gql.ts delete mode 100644 demo/admin/src/warnings/generated/WarningForm.tsx delete mode 100644 demo/admin/src/warnings/generated/WarningsGrid.tsx delete mode 100644 demo/admin/src/warnings/generated/WarningsPage.tsx diff --git a/demo/admin/src/common/MasterMenu.tsx b/demo/admin/src/common/MasterMenu.tsx index 9905291e59..562c8f6691 100644 --- a/demo/admin/src/common/MasterMenu.tsx +++ b/demo/admin/src/common/MasterMenu.tsx @@ -32,7 +32,7 @@ import { ProductsPage } from "@src/products/generated/ProductsPage"; import { ManufacturersPage as ManufacturersHandmadePage } from "@src/products/ManufacturersPage"; import ProductsHandmadePage from "@src/products/ProductsPage"; import ProductTagsPage from "@src/products/tags/ProductTagsPage"; -import { WarningsPage } from "@src/warnings/generated/WarningsPage"; +import { WarningsPage } from "@src/warnings/WarningsPage"; import { FormattedMessage } from "react-intl"; import { Redirect, RouteComponentProps } from "react-router-dom"; diff --git a/demo/admin/src/warnings/WarningsGrid.tsx b/demo/admin/src/warnings/WarningsGrid.tsx new file mode 100644 index 0000000000..1bb90e502c --- /dev/null +++ b/demo/admin/src/warnings/WarningsGrid.tsx @@ -0,0 +1,152 @@ +import { gql, useQuery } from "@apollo/client"; +import { + DataGridToolbar, + GridColDef, + GridFilterButton, + MainContent, + muiGridFilterToGql, + muiGridSortToGql, + ToolbarItem, + useBufferedRowCount, + useDataGridRemote, + usePersistentColumnState, +} from "@comet/admin"; +import { WarningSolid } from "@comet/admin-icons"; +import { Chip } from "@mui/material"; +import { DataGrid, GridToolbarQuickFilter } from "@mui/x-data-grid"; +import { GQLWarningLevel } from "@src/graphql.generated"; +import * as React from "react"; +import { FormattedDate, FormattedTime, useIntl } from "react-intl"; + +import { GQLWarningsGridQuery, GQLWarningsGridQueryVariables, GQLWarningsListFragment } from "./WarningsGrid.generated"; + +const warningsFragment = gql` + fragment WarningsList on Warning { + id + createdAt + updatedAt + type + level + state + } +`; + +const warningsQuery = gql` + query WarningsGrid($offset: Int, $limit: Int, $sort: [WarningSort!], $search: String, $filter: WarningFilter) { + warnings(offset: $offset, limit: $limit, sort: $sort, search: $search, filter: $filter) { + nodes { + ...WarningsList + } + totalCount + } + } + ${warningsFragment} +`; + +function WarningsGridToolbar() { + return ( + + + + + + + + + ); +} + +export function WarningsGrid(): React.ReactElement { + const intl = useIntl(); + const dataGridProps = { + ...useDataGridRemote({ initialFilter: { items: [{ columnField: "state", operatorValue: "is", value: "open" }] } }), + ...usePersistentColumnState("WarningsGrid"), + }; + + const columns: GridColDef[] = [ + { + field: "createdAt", + headerName: intl.formatMessage({ id: "warning.dateTime", defaultMessage: "Date / Time" }), + type: "dateTime", + renderCell: (params) => ( + <> + + + ), + width: 200, + }, + { + field: "level", + headerName: intl.formatMessage({ id: "warning.level", defaultMessage: "Level" }), + type: "singleSelect", + valueOptions: [ + { value: "critical", label: intl.formatMessage({ id: "warning.level.critical", defaultMessage: "Critical" }) }, + { value: "high", label: intl.formatMessage({ id: "warning.level.high", defaultMessage: "High" }) }, + { value: "low", label: intl.formatMessage({ id: "warning.level.low", defaultMessage: "Low" }) }, + ], + width: 150, + renderCell: (params) => { + const colorMapping: Record = { + critical: "error", + high: "warning", + low: "default", + }; + return ( + : undefined} + color={colorMapping[params.value as GQLWarningLevel]} + label={params.value} + /> + ); + }, + }, + { + field: "type", + headerName: intl.formatMessage({ id: "warning.type", defaultMessage: "Type" }), + width: 150, + renderCell: (params) => , + }, + { + field: "state", + headerName: intl.formatMessage({ id: "warning.state", defaultMessage: "State" }), + type: "singleSelect", + valueOptions: [ + { value: "open", label: intl.formatMessage({ id: "warning.state.open", defaultMessage: "Open" }) }, + { value: "resolved", label: intl.formatMessage({ id: "warning.state.resolved", defaultMessage: "Resolved" }) }, + { value: "ignored", label: intl.formatMessage({ id: "warning.state.ignored", defaultMessage: "Ignored" }) }, + ], + width: 150, + }, + ]; + + const { filter: gqlFilter, search: gqlSearch } = muiGridFilterToGql(columns, dataGridProps.filterModel); + + const { data, loading, error } = useQuery(warningsQuery, { + variables: { + filter: gqlFilter, + search: gqlSearch, + offset: dataGridProps.page * dataGridProps.pageSize, + limit: dataGridProps.pageSize, + sort: muiGridSortToGql(dataGridProps.sortModel), + }, + }); + const rowCount = useBufferedRowCount(data?.warnings.totalCount); + if (error) throw error; + const rows = data?.warnings.nodes ?? []; + + return ( + + + + ); +} diff --git a/demo/admin/src/warnings/WarningsPage.tsx b/demo/admin/src/warnings/WarningsPage.tsx new file mode 100644 index 0000000000..f1880abe57 --- /dev/null +++ b/demo/admin/src/warnings/WarningsPage.tsx @@ -0,0 +1,14 @@ +import { Stack } from "@comet/admin"; +import * as React from "react"; +import { useIntl } from "react-intl"; + +import { WarningsGrid } from "./WarningsGrid"; + +export function WarningsPage(): React.ReactElement { + const intl = useIntl(); + return ( + + + + ); +} diff --git a/demo/admin/src/warnings/generated/WarningForm.gql.ts b/demo/admin/src/warnings/generated/WarningForm.gql.ts deleted file mode 100644 index 4faa2364ef..0000000000 --- a/demo/admin/src/warnings/generated/WarningForm.gql.ts +++ /dev/null @@ -1,53 +0,0 @@ -// This file has been generated by comet admin-generator. -// You may choose to use this file as scaffold by moving this file out of generated folder and removing this comment. - -import { gql } from "@apollo/client"; - -export const warningFormFragment = gql` - fragment WarningForm on Warning { - type - level - state - } -`; - -export const warningFormQuery = gql` - query WarningForm($id: ID!) { - warning(id: $id) { - id - updatedAt - ...WarningForm - } - } - ${warningFormFragment} -`; - -export const warningFormCheckForChangesQuery = gql` - query WarningFormCheckForChanges($id: ID!) { - warning(id: $id) { - updatedAt - } - } -`; - -export const createWarningMutation = gql` - mutation CreateWarning($input: WarningInput!) { - createWarning(input: $input) { - id - updatedAt - ...WarningForm - } - } - ${warningFormFragment} -`; - -export const updateWarningMutation = gql` - mutation UpdateWarning($id: ID!, $input: WarningUpdateInput!) { - updateWarning(id: $id, input: $input) { - id - updatedAt - ...WarningForm - } - } - ${warningFormFragment} -`; diff --git a/demo/admin/src/warnings/generated/WarningForm.tsx b/demo/admin/src/warnings/generated/WarningForm.tsx deleted file mode 100644 index 1b031ed650..0000000000 --- a/demo/admin/src/warnings/generated/WarningForm.tsx +++ /dev/null @@ -1,182 +0,0 @@ -// This file has been generated by comet admin-generator. -// You may choose to use this file as scaffold by moving this file out of generated folder and removing this comment. - -import { useApolloClient, useQuery } from "@apollo/client"; -import { - Field, - filterByFragment, - FinalForm, - FinalFormSaveButton, - FinalFormSelect, - FinalFormSubmitEvent, - Loading, - MainContent, - TextField, - Toolbar, - ToolbarActions, - ToolbarFillSpace, - ToolbarItem, - ToolbarTitleItem, - useFormApiRef, - useStackApi, - useStackSwitchApi, -} from "@comet/admin"; -import { ArrowLeft } from "@comet/admin-icons"; -import { ContentScopeIndicator, queryUpdatedAt, resolveHasSaveConflict, useFormSaveConflict } from "@comet/cms-admin"; -import { IconButton, MenuItem } from "@mui/material"; -import { FormApi } from "final-form"; -import React from "react"; -import { FormattedMessage } from "react-intl"; - -import { createWarningMutation, updateWarningMutation, warningFormFragment, warningFormQuery } from "./WarningForm.gql"; -import { - GQLCreateWarningMutation, - GQLCreateWarningMutationVariables, - GQLUpdateWarningMutation, - GQLUpdateWarningMutationVariables, - GQLWarningFormFragment, - GQLWarningFormQuery, - GQLWarningFormQueryVariables, -} from "./WarningForm.gql.generated"; - -type FormValues = GQLWarningFormFragment; - -interface FormProps { - id?: string; -} - -export function WarningForm({ id }: FormProps): React.ReactElement { - const stackApi = useStackApi(); - const client = useApolloClient(); - const mode = id ? "edit" : "add"; - const formApiRef = useFormApiRef(); - const stackSwitchApi = useStackSwitchApi(); - - const { data, error, loading, refetch } = useQuery( - warningFormQuery, - id ? { variables: { id } } : { skip: true }, - ); - - const initialValues = React.useMemo>( - () => - data?.warning - ? { - ...filterByFragment(warningFormFragment, data.warning), - } - : {}, - [data], - ); - - const saveConflict = useFormSaveConflict({ - checkConflict: async () => { - const updatedAt = await queryUpdatedAt(client, "warning", id); - return resolveHasSaveConflict(data?.warning.updatedAt, updatedAt); - }, - formApiRef, - loadLatestVersion: async () => { - await refetch(); - }, - }); - - const handleSubmit = async (state: FormValues, form: FormApi, event: FinalFormSubmitEvent) => { - if (await saveConflict.checkForConflicts()) { - throw new Error("Conflicts detected"); - } - - const output = { - ...state, - }; - - if (mode === "edit") { - if (!id) { - throw new Error("Missing id in edit mode"); - } - await client.mutate({ - mutation: updateWarningMutation, - variables: { id, input: output }, - }); - } else { - const { data: mutationResponse } = await client.mutate({ - mutation: createWarningMutation, - variables: { input: output }, - }); - if (!event.navigatingBack) { - const id = mutationResponse?.createWarning.id; - if (id) { - setTimeout(() => { - stackSwitchApi.activatePage("edit", id); - }); - } - } - } - }; - - if (error) throw error; - - if (loading) { - return ; - } - - return ( - apiRef={formApiRef} onSubmit={handleSubmit} mode={mode} initialValues={initialValues}> - {({ values }) => ( - <> - {saveConflict.dialogs} - }> - - - - - - - - - - - - - - - } - /> - }> - {(props) => ( - - - - - - - - - - - - )} - - }> - {(props) => ( - - - - - - - - - - - - )} - - - - )} - - ); -} diff --git a/demo/admin/src/warnings/generated/WarningsGrid.tsx b/demo/admin/src/warnings/generated/WarningsGrid.tsx deleted file mode 100644 index b5f7552fdb..0000000000 --- a/demo/admin/src/warnings/generated/WarningsGrid.tsx +++ /dev/null @@ -1,206 +0,0 @@ -// This file has been generated by comet admin-generator. -// You may choose to use this file as scaffold by moving this file out of generated folder and removing this comment. -import { gql, useApolloClient, useQuery } from "@apollo/client"; -import { - CrudContextMenu, - DataGridToolbar, - GridColDef, - GridFilterButton, - MainContent, - muiGridFilterToGql, - muiGridSortToGql, - StackLink, - ToolbarActions, - ToolbarFillSpace, - ToolbarItem, - useBufferedRowCount, - useDataGridRemote, - usePersistentColumnState, -} from "@comet/admin"; -import { Add as AddIcon, Edit } from "@comet/admin-icons"; -import { Button, IconButton } from "@mui/material"; -import { DataGridPro, GridToolbarQuickFilter } from "@mui/x-data-grid-pro"; -import * as React from "react"; -import { FormattedMessage, useIntl } from "react-intl"; - -import { - GQLCreateWarningMutation, - GQLCreateWarningMutationVariables, - GQLDeleteWarningMutation, - GQLDeleteWarningMutationVariables, - GQLWarningsGridQuery, - GQLWarningsGridQueryVariables, - GQLWarningsListFragment, -} from "./WarningsGrid.generated"; - -const warningsFragment = gql` - fragment WarningsList on Warning { - id - createdAt - updatedAt - type - level - state - } -`; - -const warningsQuery = gql` - query WarningsGrid($offset: Int, $limit: Int, $sort: [WarningSort!], $search: String, $filter: WarningFilter) { - warnings(offset: $offset, limit: $limit, sort: $sort, search: $search, filter: $filter) { - nodes { - ...WarningsList - } - totalCount - } - } - ${warningsFragment} -`; - -const deleteWarningMutation = gql` - mutation DeleteWarning($id: ID!) { - deleteWarning(id: $id) - } -`; - -const createWarningMutation = gql` - mutation CreateWarning($input: WarningInput!) { - createWarning(input: $input) { - id - } - } -`; - -function WarningsGridToolbar() { - return ( - - - - - - - - - - - - - ); -} - -export function WarningsGrid(): React.ReactElement { - const client = useApolloClient(); - const intl = useIntl(); - const dataGridProps = { ...useDataGridRemote(), ...usePersistentColumnState("WarningsGrid") }; - - const columns: GridColDef[] = [ - { - field: "createdAt", - headerName: intl.formatMessage({ id: "warning.createdAt", defaultMessage: "Created At" }), - type: "dateTime", - valueGetter: ({ value }) => value && new Date(value), - width: 150, - }, - { - field: "updatedAt", - headerName: intl.formatMessage({ id: "warning.updatedAt", defaultMessage: "Updated At" }), - type: "dateTime", - valueGetter: ({ value }) => value && new Date(value), - width: 150, - }, - { field: "type", headerName: intl.formatMessage({ id: "warning.type", defaultMessage: "Type" }), width: 150 }, - { - field: "level", - headerName: intl.formatMessage({ id: "warning.level", defaultMessage: "Level" }), - type: "singleSelect", - valueOptions: [ - { value: "critical", label: intl.formatMessage({ id: "warning.level.critical", defaultMessage: "Critical" }) }, - { value: "high", label: intl.formatMessage({ id: "warning.level.high", defaultMessage: "High" }) }, - { value: "low", label: intl.formatMessage({ id: "warning.level.low", defaultMessage: "Low" }) }, - ], - width: 150, - }, - { - field: "state", - headerName: intl.formatMessage({ id: "warning.state", defaultMessage: "State" }), - type: "singleSelect", - valueOptions: [ - { value: "open", label: intl.formatMessage({ id: "warning.state.open", defaultMessage: "Open" }) }, - { value: "resolved", label: intl.formatMessage({ id: "warning.state.resolved", defaultMessage: "Resolved" }) }, - { value: "ignored", label: intl.formatMessage({ id: "warning.state.ignored", defaultMessage: "Ignored" }) }, - ], - width: 150, - }, - { - field: "actions", - headerName: "", - sortable: false, - filterable: false, - type: "actions", - renderCell: (params) => { - return ( - <> - - - - { - const row = params.row; - return { - type: row.type, - level: row.level, - state: row.state, - }; - }} - onPaste={async ({ input }) => { - await client.mutate({ - mutation: createWarningMutation, - variables: { input }, - }); - }} - onDelete={async () => { - await client.mutate({ - mutation: deleteWarningMutation, - variables: { id: params.row.id }, - }); - }} - refetchQueries={[warningsQuery]} - /> - - ); - }, - }, - ]; - - const { filter: gqlFilter, search: gqlSearch } = muiGridFilterToGql(columns, dataGridProps.filterModel); - - const { data, loading, error } = useQuery(warningsQuery, { - variables: { - filter: gqlFilter, - search: gqlSearch, - offset: dataGridProps.page * dataGridProps.pageSize, - limit: dataGridProps.pageSize, - sort: muiGridSortToGql(dataGridProps.sortModel), - }, - }); - const rowCount = useBufferedRowCount(data?.warnings.totalCount); - if (error) throw error; - const rows = data?.warnings.nodes ?? []; - - return ( - - - - ); -} diff --git a/demo/admin/src/warnings/generated/WarningsPage.tsx b/demo/admin/src/warnings/generated/WarningsPage.tsx deleted file mode 100644 index d639b8e19b..0000000000 --- a/demo/admin/src/warnings/generated/WarningsPage.tsx +++ /dev/null @@ -1,30 +0,0 @@ -// This file has been generated by comet admin-generator. -// You may choose to use this file as scaffold by moving this file out of generated folder and removing this comment. - -import { Stack, StackPage, StackSwitch, StackToolbar } from "@comet/admin"; -import { ContentScopeIndicator } from "@comet/cms-admin"; -import * as React from "react"; -import { useIntl } from "react-intl"; - -import { WarningForm } from "./WarningForm"; -import { WarningsGrid } from "./WarningsGrid"; - -export function WarningsPage(): React.ReactElement { - const intl = useIntl(); - return ( - - - - } /> - - - - {(selectedId) => } - - - - - - - ); -} From c28f4287df0db528c7f9753208abb0c0f0d695a0 Mon Sep 17 00:00:00 2001 From: Raphael Blum Date: Tue, 8 Oct 2024 08:47:48 +0200 Subject: [PATCH 3/6] rename enums --- ...level.enum.ts => warning-severity.enum.ts} | 6 +++--- ...g-state.enum.ts => warning-status.enum.ts} | 6 +++--- .../src/warnings/entities/warning.entity.ts | 16 +++++++-------- .../warnings/generated/dto/warning.filter.ts | 20 +++++++++---------- .../warnings/generated/dto/warning.input.ts | 16 +++++++-------- .../warnings/generated/dto/warning.sort.ts | 4 ++-- .../warnings/generated/dto/warnings.args.ts | 7 ++++++- .../warnings/generated/warning.resolver.ts | 3 ++- 8 files changed, 42 insertions(+), 36 deletions(-) rename demo/api/src/warnings/entities/{warning-level.enum.ts => warning-severity.enum.ts} (56%) rename demo/api/src/warnings/entities/{warning-state.enum.ts => warning-status.enum.ts} (59%) diff --git a/demo/api/src/warnings/entities/warning-level.enum.ts b/demo/api/src/warnings/entities/warning-severity.enum.ts similarity index 56% rename from demo/api/src/warnings/entities/warning-level.enum.ts rename to demo/api/src/warnings/entities/warning-severity.enum.ts index 1b71249125..e00efb56d9 100644 --- a/demo/api/src/warnings/entities/warning-level.enum.ts +++ b/demo/api/src/warnings/entities/warning-severity.enum.ts @@ -1,10 +1,10 @@ import { registerEnumType } from "@nestjs/graphql"; -export enum WarningLevel { +export enum WarningSeverity { critical = "critical", high = "high", low = "low", } -registerEnumType(WarningLevel, { - name: "WarningLevel", +registerEnumType(WarningSeverity, { + name: "WarningSeverity", }); diff --git a/demo/api/src/warnings/entities/warning-state.enum.ts b/demo/api/src/warnings/entities/warning-status.enum.ts similarity index 59% rename from demo/api/src/warnings/entities/warning-state.enum.ts rename to demo/api/src/warnings/entities/warning-status.enum.ts index f54cad8b37..ddd16d4262 100644 --- a/demo/api/src/warnings/entities/warning-state.enum.ts +++ b/demo/api/src/warnings/entities/warning-status.enum.ts @@ -1,10 +1,10 @@ import { registerEnumType } from "@nestjs/graphql"; -export enum WarningState { +export enum WarningStatus { open = "open", resolved = "resolved", ignored = "ignored", } -registerEnumType(WarningState, { - name: "WarningState", +registerEnumType(WarningStatus, { + name: "WarningStatus", }); diff --git a/demo/api/src/warnings/entities/warning.entity.ts b/demo/api/src/warnings/entities/warning.entity.ts index 68265aaf87..2144b542c0 100644 --- a/demo/api/src/warnings/entities/warning.entity.ts +++ b/demo/api/src/warnings/entities/warning.entity.ts @@ -4,8 +4,8 @@ import { BaseEntity, Entity, Enum, OptionalProps, PrimaryKey, Property } from "@ import { Field, ID, ObjectType } from "@nestjs/graphql"; import { v4 as uuid } from "uuid"; -import { WarningLevel } from "./warning-level.enum"; -import { WarningState } from "./warning-state.enum"; +import { WarningSeverity } from "./warning-severity.enum"; +import { WarningStatus } from "./warning-status.enum"; @ObjectType() @Entity() @@ -31,13 +31,13 @@ export class Warning extends BaseEntity { @CrudField() type: string; - @Enum({ items: () => WarningLevel }) - @Field(() => WarningLevel) - level: WarningLevel; + @Enum({ items: () => WarningSeverity }) + @Field(() => WarningSeverity) + severity: WarningSeverity; // TODO: add blockInfos with COM-958 - @Enum({ items: () => WarningState }) - @Field(() => WarningState) - state: WarningState; + @Enum({ items: () => WarningStatus }) + @Field(() => WarningStatus) + status: WarningStatus; } diff --git a/demo/api/src/warnings/generated/dto/warning.filter.ts b/demo/api/src/warnings/generated/dto/warning.filter.ts index 038aca5b58..1459ae24bf 100644 --- a/demo/api/src/warnings/generated/dto/warning.filter.ts +++ b/demo/api/src/warnings/generated/dto/warning.filter.ts @@ -5,13 +5,13 @@ import { Field, InputType } from "@nestjs/graphql"; import { Type } from "class-transformer"; import { IsOptional, ValidateNested } from "class-validator"; -import { WarningLevel } from "../../entities/warning-level.enum"; -import { WarningState } from "../../entities/warning-state.enum"; +import { WarningSeverity } from "../../entities/warning-severity.enum"; +import { WarningStatus } from "../../entities/warning-status.enum"; @InputType() -class WarningLevelEnumFilter extends createEnumFilter(WarningLevel) {} +class WarningSeverityEnumFilter extends createEnumFilter(WarningSeverity) {} @InputType() -class WarningStateEnumFilter extends createEnumFilter(WarningState) {} +class WarningStatusEnumFilter extends createEnumFilter(WarningStatus) {} @InputType() export class WarningFilter { @@ -33,17 +33,17 @@ export class WarningFilter { @Type(() => StringFilter) type?: StringFilter; - @Field(() => WarningLevelEnumFilter, { nullable: true }) + @Field(() => WarningSeverityEnumFilter, { nullable: true }) @ValidateNested() @IsOptional() - @Type(() => WarningLevelEnumFilter) - level?: WarningLevelEnumFilter; + @Type(() => WarningSeverityEnumFilter) + severity?: WarningSeverityEnumFilter; - @Field(() => WarningStateEnumFilter, { nullable: true }) + @Field(() => WarningStatusEnumFilter, { nullable: true }) @ValidateNested() @IsOptional() - @Type(() => WarningStateEnumFilter) - state?: WarningStateEnumFilter; + @Type(() => WarningStatusEnumFilter) + status?: WarningStatusEnumFilter; @Field(() => [WarningFilter], { nullable: true }) @Type(() => WarningFilter) diff --git a/demo/api/src/warnings/generated/dto/warning.input.ts b/demo/api/src/warnings/generated/dto/warning.input.ts index 16e7050149..59f970c480 100644 --- a/demo/api/src/warnings/generated/dto/warning.input.ts +++ b/demo/api/src/warnings/generated/dto/warning.input.ts @@ -4,8 +4,8 @@ import { PartialType } from "@comet/cms-api"; import { Field, InputType } from "@nestjs/graphql"; import { IsEnum, IsNotEmpty, IsString } from "class-validator"; -import { WarningLevel } from "../../entities/warning-level.enum"; -import { WarningState } from "../../entities/warning-state.enum"; +import { WarningSeverity } from "../../entities/warning-severity.enum"; +import { WarningStatus } from "../../entities/warning-status.enum"; @InputType() export class WarningInput { @@ -15,14 +15,14 @@ export class WarningInput { type: string; @IsNotEmpty() - @IsEnum(WarningLevel) - @Field(() => WarningLevel) - level: WarningLevel; + @IsEnum(WarningSeverity) + @Field(() => WarningSeverity) + severity: WarningSeverity; @IsNotEmpty() - @IsEnum(WarningState) - @Field(() => WarningState) - state: WarningState; + @IsEnum(WarningStatus) + @Field(() => WarningStatus) + status: WarningStatus; } @InputType() diff --git a/demo/api/src/warnings/generated/dto/warning.sort.ts b/demo/api/src/warnings/generated/dto/warning.sort.ts index c68e246aad..827d87a132 100644 --- a/demo/api/src/warnings/generated/dto/warning.sort.ts +++ b/demo/api/src/warnings/generated/dto/warning.sort.ts @@ -8,8 +8,8 @@ export enum WarningSortField { createdAt = "createdAt", updatedAt = "updatedAt", type = "type", - level = "level", - state = "state", + severity = "severity", + status = "status", } registerEnumType(WarningSortField, { name: "WarningSortField", diff --git a/demo/api/src/warnings/generated/dto/warnings.args.ts b/demo/api/src/warnings/generated/dto/warnings.args.ts index 9e283c274f..863845c212 100644 --- a/demo/api/src/warnings/generated/dto/warnings.args.ts +++ b/demo/api/src/warnings/generated/dto/warnings.args.ts @@ -3,13 +3,18 @@ import { OffsetBasedPaginationArgs } from "@comet/cms-api"; import { ArgsType, Field } from "@nestjs/graphql"; import { Type } from "class-transformer"; -import { IsOptional, IsString, ValidateNested } from "class-validator"; +import { IsEnum, IsOptional, IsString, ValidateNested } from "class-validator"; +import { WarningStatus } from "../../entities/warning-status.enum"; import { WarningFilter } from "./warning.filter"; import { WarningSort } from "./warning.sort"; @ArgsType() export class WarningsArgs extends OffsetBasedPaginationArgs { + @Field(() => [WarningStatus], { defaultValue: [undefined] }) + @IsEnum(WarningStatus, { each: true }) + status: WarningStatus[]; + @Field({ nullable: true }) @IsOptional() @IsString() diff --git a/demo/api/src/warnings/generated/warning.resolver.ts b/demo/api/src/warnings/generated/warning.resolver.ts index ec3c11e7ba..83c6bbc590 100644 --- a/demo/api/src/warnings/generated/warning.resolver.ts +++ b/demo/api/src/warnings/generated/warning.resolver.ts @@ -24,8 +24,9 @@ export class WarningResolver { } @Query(() => PaginatedWarnings) - async warnings(@Args() { search, filter, sort, offset, limit }: WarningsArgs): Promise { + async warnings(@Args() { status, search, filter, sort, offset, limit }: WarningsArgs): Promise { const where = gqlArgsToMikroOrmQuery({ search, filter }, this.repository); + where.status = { $in: status }; const options: FindOptions = { offset, limit }; From 78c8ba05d7d9b0cda13f933a3c117b045ee258e1 Mon Sep 17 00:00:00 2001 From: Raphael Blum Date: Tue, 8 Oct 2024 08:55:23 +0200 Subject: [PATCH 4/6] change requiredPermission to own permission for warnings --- demo/api/src/warnings/entities/warning.entity.ts | 2 +- demo/api/src/warnings/generated/warning.resolver.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/demo/api/src/warnings/entities/warning.entity.ts b/demo/api/src/warnings/entities/warning.entity.ts index 2144b542c0..c41fff054e 100644 --- a/demo/api/src/warnings/entities/warning.entity.ts +++ b/demo/api/src/warnings/entities/warning.entity.ts @@ -10,7 +10,7 @@ import { WarningStatus } from "./warning-status.enum"; @ObjectType() @Entity() @RootBlockEntity() -@CrudGenerator({ targetDirectory: `${__dirname}/../generated/`, requiredPermission: ["pageTree"] }) +@CrudGenerator({ targetDirectory: `${__dirname}/../generated/`, requiredPermission: ["warnings"] }) export class Warning extends BaseEntity { [OptionalProps]?: "createdAt" | "updatedAt" | "status"; diff --git a/demo/api/src/warnings/generated/warning.resolver.ts b/demo/api/src/warnings/generated/warning.resolver.ts index 83c6bbc590..7f37964b1a 100644 --- a/demo/api/src/warnings/generated/warning.resolver.ts +++ b/demo/api/src/warnings/generated/warning.resolver.ts @@ -12,7 +12,7 @@ import { WarningInput, WarningUpdateInput } from "./dto/warning.input"; import { WarningsArgs } from "./dto/warnings.args"; @Resolver(() => Warning) -@RequiredPermission(["pageTree"], { skipScopeCheck: true }) +@RequiredPermission(["warnings"], { skipScopeCheck: true }) export class WarningResolver { constructor(private readonly entityManager: EntityManager, @InjectRepository(Warning) private readonly repository: EntityRepository) {} From 494d852ba66c7e6f96e7869aae930c8c32cfb9bd Mon Sep 17 00:00:00 2001 From: Raphael Blum Date: Tue, 8 Oct 2024 12:48:02 +0200 Subject: [PATCH 5/6] update Migration, run demo again for schema changes and adapt Grid due to api generator changes --- demo/admin/src/warnings/WarningsGrid.tsx | 32 +++++++------- demo/api/schema.gql | 42 +++++++++---------- .../db/migrations/Migration20240725161414.ts | 13 ------ .../db/migrations/Migration20241008102605.ts | 9 ++++ 4 files changed, 46 insertions(+), 50 deletions(-) delete mode 100644 demo/api/src/db/migrations/Migration20240725161414.ts create mode 100644 demo/api/src/db/migrations/Migration20241008102605.ts diff --git a/demo/admin/src/warnings/WarningsGrid.tsx b/demo/admin/src/warnings/WarningsGrid.tsx index 1bb90e502c..1df6593e5b 100644 --- a/demo/admin/src/warnings/WarningsGrid.tsx +++ b/demo/admin/src/warnings/WarningsGrid.tsx @@ -14,7 +14,7 @@ import { import { WarningSolid } from "@comet/admin-icons"; import { Chip } from "@mui/material"; import { DataGrid, GridToolbarQuickFilter } from "@mui/x-data-grid"; -import { GQLWarningLevel } from "@src/graphql.generated"; +import { GQLWarningSeverity } from "@src/graphql.generated"; import * as React from "react"; import { FormattedDate, FormattedTime, useIntl } from "react-intl"; @@ -26,14 +26,14 @@ const warningsFragment = gql` createdAt updatedAt type - level - state + severity + status } `; const warningsQuery = gql` query WarningsGrid($offset: Int, $limit: Int, $sort: [WarningSort!], $search: String, $filter: WarningFilter) { - warnings(offset: $offset, limit: $limit, sort: $sort, search: $search, filter: $filter) { + warnings(offset: $offset, limit: $limit, sort: $sort, search: $search, filter: $filter, status: [open, resolved, ignored]) { nodes { ...WarningsList } @@ -76,17 +76,17 @@ export function WarningsGrid(): React.ReactElement { width: 200, }, { - field: "level", - headerName: intl.formatMessage({ id: "warning.level", defaultMessage: "Level" }), + field: "severity", + headerName: intl.formatMessage({ id: "warning.severity", defaultMessage: "Severity" }), type: "singleSelect", valueOptions: [ - { value: "critical", label: intl.formatMessage({ id: "warning.level.critical", defaultMessage: "Critical" }) }, - { value: "high", label: intl.formatMessage({ id: "warning.level.high", defaultMessage: "High" }) }, - { value: "low", label: intl.formatMessage({ id: "warning.level.low", defaultMessage: "Low" }) }, + { value: "critical", label: intl.formatMessage({ id: "warning.severity.critical", defaultMessage: "Critical" }) }, + { value: "high", label: intl.formatMessage({ id: "warning.severity.high", defaultMessage: "High" }) }, + { value: "low", label: intl.formatMessage({ id: "warning.severity.low", defaultMessage: "Low" }) }, ], width: 150, renderCell: (params) => { - const colorMapping: Record = { + const colorMapping: Record = { critical: "error", high: "warning", low: "default", @@ -94,7 +94,7 @@ export function WarningsGrid(): React.ReactElement { return ( : undefined} - color={colorMapping[params.value as GQLWarningLevel]} + color={colorMapping[params.value as GQLWarningSeverity]} label={params.value} /> ); @@ -107,13 +107,13 @@ export function WarningsGrid(): React.ReactElement { renderCell: (params) => , }, { - field: "state", - headerName: intl.formatMessage({ id: "warning.state", defaultMessage: "State" }), + field: "status", + headerName: intl.formatMessage({ id: "warning.status", defaultMessage: "Status" }), type: "singleSelect", valueOptions: [ - { value: "open", label: intl.formatMessage({ id: "warning.state.open", defaultMessage: "Open" }) }, - { value: "resolved", label: intl.formatMessage({ id: "warning.state.resolved", defaultMessage: "Resolved" }) }, - { value: "ignored", label: intl.formatMessage({ id: "warning.state.ignored", defaultMessage: "Ignored" }) }, + { value: "open", label: intl.formatMessage({ id: "warning.status.open", defaultMessage: "Open" }) }, + { value: "resolved", label: intl.formatMessage({ id: "warning.status.resolved", defaultMessage: "Resolved" }) }, + { value: "ignored", label: intl.formatMessage({ id: "warning.status.ignored", defaultMessage: "Ignored" }) }, ], width: 150, }, diff --git a/demo/api/schema.gql b/demo/api/schema.gql index 5cf2ece4ce..6e9168c115 100644 --- a/demo/api/schema.gql +++ b/demo/api/schema.gql @@ -610,17 +610,17 @@ type Warning { createdAt: DateTime! updatedAt: DateTime! type: String! - level: WarningLevel! - state: WarningState! + severity: WarningSeverity! + status: WarningStatus! } -enum WarningLevel { +enum WarningSeverity { critical high low } -enum WarningState { +enum WarningStatus { open resolved ignored @@ -817,7 +817,7 @@ type Query { manufacturerCountry(id: ID!): ManufacturerCountry! manufacturerCountries(offset: Int! = 0, limit: Int! = 25, search: String, filter: ManufacturerCountryFilter): PaginatedManufacturerCountries! warning(id: ID!): Warning! - warnings(offset: Int! = 0, limit: Int! = 25, search: String, filter: WarningFilter, sort: [WarningSort!]): PaginatedWarnings! + warnings(offset: Int! = 0, limit: Int! = 25, status: [WarningStatus!]! = [], search: String, filter: WarningFilter, sort: [WarningSort!]): PaginatedWarnings! } input UserFilter { @@ -1181,22 +1181,22 @@ input WarningFilter { createdAt: DateTimeFilter updatedAt: DateTimeFilter type: StringFilter - level: WarningLevelEnumFilter - state: WarningStateEnumFilter + severity: WarningSeverityEnumFilter + status: WarningStatusEnumFilter and: [WarningFilter!] or: [WarningFilter!] } -input WarningLevelEnumFilter { - isAnyOf: [WarningLevel!] - equal: WarningLevel - notEqual: WarningLevel +input WarningSeverityEnumFilter { + isAnyOf: [WarningSeverity!] + equal: WarningSeverity + notEqual: WarningSeverity } -input WarningStateEnumFilter { - isAnyOf: [WarningState!] - equal: WarningState - notEqual: WarningState +input WarningStatusEnumFilter { + isAnyOf: [WarningStatus!] + equal: WarningStatus + notEqual: WarningStatus } input WarningSort { @@ -1208,8 +1208,8 @@ enum WarningSortField { createdAt updatedAt type - level - state + severity + status } type Mutation { @@ -1567,12 +1567,12 @@ input ManufacturerUpdateInput { input WarningInput { type: String! - level: WarningLevel! - state: WarningState! + severity: WarningSeverity! + status: WarningStatus! } input WarningUpdateInput { type: String - level: WarningLevel - state: WarningState + severity: WarningSeverity + status: WarningStatus } diff --git a/demo/api/src/db/migrations/Migration20240725161414.ts b/demo/api/src/db/migrations/Migration20240725161414.ts deleted file mode 100644 index ecf18e67b8..0000000000 --- a/demo/api/src/db/migrations/Migration20240725161414.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Migration } from '@mikro-orm/migrations'; - -export class Migration20240725161414 extends Migration { - - async up(): Promise { - this.addSql('create table "Warning" ("id" uuid not null, "createdAt" timestamptz(0) not null, "updatedAt" timestamptz(0) not null, "type" varchar(255) not null, "level" text check ("level" in (\'critical\', \'high\', \'low\')) not null, "state" text check ("state" in (\'open\', \'resolved\', \'ignored\')) not null, constraint "Warning_pkey" primary key ("id"));'); - } - - async down(): Promise { - this.addSql('drop table if exists "Warning" cascade;'); - } - -} diff --git a/demo/api/src/db/migrations/Migration20241008102605.ts b/demo/api/src/db/migrations/Migration20241008102605.ts new file mode 100644 index 0000000000..f1bf384f90 --- /dev/null +++ b/demo/api/src/db/migrations/Migration20241008102605.ts @@ -0,0 +1,9 @@ +import { Migration } from '@mikro-orm/migrations'; + +export class Migration20241008102605 extends Migration { + + async up(): Promise { + this.addSql('create table "Warning" ("id" uuid not null, "createdAt" timestamptz(0) not null, "updatedAt" timestamptz(0) not null, "type" varchar(255) not null, "severity" text check ("severity" in (\'critical\', \'high\', \'low\')) not null, "status" text check ("status" in (\'open\', \'resolved\', \'ignored\')) not null, constraint "Warning_pkey" primary key ("id"));'); + } + +} From faec2eff65a16bac5a30c1af8fe4d3afad87c0e8 Mon Sep 17 00:00:00 2001 From: Raphael Blum Date: Fri, 11 Oct 2024 09:27:53 +0200 Subject: [PATCH 6/6] set default for warnings status --- demo/admin/src/warnings/WarningsGrid.tsx | 2 +- demo/api/schema.gql | 4 ++-- demo/api/src/warnings/entities/warning.entity.ts | 2 +- demo/api/src/warnings/generated/dto/warning.input.ts | 2 +- demo/api/src/warnings/generated/dto/warnings.args.ts | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/demo/admin/src/warnings/WarningsGrid.tsx b/demo/admin/src/warnings/WarningsGrid.tsx index 1df6593e5b..3303d83268 100644 --- a/demo/admin/src/warnings/WarningsGrid.tsx +++ b/demo/admin/src/warnings/WarningsGrid.tsx @@ -33,7 +33,7 @@ const warningsFragment = gql` const warningsQuery = gql` query WarningsGrid($offset: Int, $limit: Int, $sort: [WarningSort!], $search: String, $filter: WarningFilter) { - warnings(offset: $offset, limit: $limit, sort: $sort, search: $search, filter: $filter, status: [open, resolved, ignored]) { + warnings(offset: $offset, limit: $limit, sort: $sort, search: $search, filter: $filter) { nodes { ...WarningsList } diff --git a/demo/api/schema.gql b/demo/api/schema.gql index 6e9168c115..f9f30bec6a 100644 --- a/demo/api/schema.gql +++ b/demo/api/schema.gql @@ -817,7 +817,7 @@ type Query { manufacturerCountry(id: ID!): ManufacturerCountry! manufacturerCountries(offset: Int! = 0, limit: Int! = 25, search: String, filter: ManufacturerCountryFilter): PaginatedManufacturerCountries! warning(id: ID!): Warning! - warnings(offset: Int! = 0, limit: Int! = 25, status: [WarningStatus!]! = [], search: String, filter: WarningFilter, sort: [WarningSort!]): PaginatedWarnings! + warnings(offset: Int! = 0, limit: Int! = 25, status: [WarningStatus!]! = [open], search: String, filter: WarningFilter, sort: [WarningSort!]): PaginatedWarnings! } input UserFilter { @@ -1568,7 +1568,7 @@ input ManufacturerUpdateInput { input WarningInput { type: String! severity: WarningSeverity! - status: WarningStatus! + status: WarningStatus! = open } input WarningUpdateInput { diff --git a/demo/api/src/warnings/entities/warning.entity.ts b/demo/api/src/warnings/entities/warning.entity.ts index c41fff054e..4e506205ca 100644 --- a/demo/api/src/warnings/entities/warning.entity.ts +++ b/demo/api/src/warnings/entities/warning.entity.ts @@ -39,5 +39,5 @@ export class Warning extends BaseEntity { @Enum({ items: () => WarningStatus }) @Field(() => WarningStatus) - status: WarningStatus; + status: WarningStatus = WarningStatus.open; } diff --git a/demo/api/src/warnings/generated/dto/warning.input.ts b/demo/api/src/warnings/generated/dto/warning.input.ts index 59f970c480..87c05b14b0 100644 --- a/demo/api/src/warnings/generated/dto/warning.input.ts +++ b/demo/api/src/warnings/generated/dto/warning.input.ts @@ -21,7 +21,7 @@ export class WarningInput { @IsNotEmpty() @IsEnum(WarningStatus) - @Field(() => WarningStatus) + @Field(() => WarningStatus, { defaultValue: WarningStatus.open }) status: WarningStatus; } diff --git a/demo/api/src/warnings/generated/dto/warnings.args.ts b/demo/api/src/warnings/generated/dto/warnings.args.ts index 863845c212..8992ebeb75 100644 --- a/demo/api/src/warnings/generated/dto/warnings.args.ts +++ b/demo/api/src/warnings/generated/dto/warnings.args.ts @@ -11,7 +11,7 @@ import { WarningSort } from "./warning.sort"; @ArgsType() export class WarningsArgs extends OffsetBasedPaginationArgs { - @Field(() => [WarningStatus], { defaultValue: [undefined] }) + @Field(() => [WarningStatus], { defaultValue: [WarningStatus.open] }) @IsEnum(WarningStatus, { each: true }) status: WarningStatus[];