diff --git a/.eslintrc.json b/.eslintrc.json index c989ea33..d274af6f 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -56,7 +56,8 @@ "type:config", "type:graphql", "type:state", - "type:types" + "type:types", + "type:shared" ] }, { diff --git a/packages/core/config/src/lib/constants/router.ts b/packages/core/config/src/lib/constants/router.ts index 266ae88e..51959d1d 100644 --- a/packages/core/config/src/lib/constants/router.ts +++ b/packages/core/config/src/lib/constants/router.ts @@ -307,18 +307,33 @@ export const ROUTER: Readonly = { getLink: () => ['', 'management', 'iam'], children: { index: { - path: '', - link: '/management/iam', - getLink: () => ['', 'management', 'iam'], + path: 'index', + link: '/management/iam/index', + getLink: () => ['', 'management', 'iam', 'index'], title: 'IAM', }, - iam: { - path: ':id', - link: '/management/iam/:id', - title: 'IAM', + create: { + path: 'create', + link: '/management/iam/create', + title: 'Create User', + getLink: () => ['', 'management', 'iam', 'create'], + }, + view: { + path: ':id/view', + link: '/management/iam/:id/view', + title: 'User', getLink: (params?: { id?: number | string }) => params?.id - ? ['', 'management', 'iam', params.id] + ? ['', 'management', 'iam', params.id, 'view'] + : ['', 'management', 'iam'], + }, + edit: { + path: ':id/edit', + link: '/management/iam/:id/edit', + title: 'Edit User', + getLink: (params?: { id?: number | string }) => + params?.id + ? ['', 'management', 'iam', params.id, 'edit'] : ['', 'management', 'iam'], }, }, @@ -529,7 +544,7 @@ export const ROUTER: Readonly = { getLink: () => ['', 'management', 'access-control', 'roles'], children: { index: { - path: '', + path: 'index', link: '/management/access-control/roles', getLink: () => [ '', @@ -539,9 +554,21 @@ export const ROUTER: Readonly = { ], title: 'Roles', }, - roles: { - path: ':id', - link: '/management/access-control/roles/:id', + create: { + path: 'create', + link: '/management/access-control/roles/create', + title: 'Create Role', + getLink: () => [ + '', + 'management', + 'access-control', + 'roles', + 'create', + ], + }, + view: { + path: ':id/view', + link: '/management/access-control/roles/:id/view', title: 'Role', getLink: (params?: { id?: number | string }) => params?.id @@ -551,6 +578,23 @@ export const ROUTER: Readonly = { 'access-control', 'roles', params.id, + 'view', + ] + : ['', 'management', 'access-control', 'roles'], + }, + edit: { + path: ':id/edit', + link: '/management/access-control/roles/:id/edit', + title: 'Edit Role', + getLink: (params?: { id?: number | string }) => + params?.id + ? [ + '', + 'management', + 'access-control', + 'roles', + params.id, + 'edit', ] : ['', 'management', 'access-control', 'roles'], }, diff --git a/packages/core/config/src/lib/constants/store.ts b/packages/core/config/src/lib/constants/store.ts index 6ad7058a..f88f7051 100755 --- a/packages/core/config/src/lib/constants/store.ts +++ b/packages/core/config/src/lib/constants/store.ts @@ -9,10 +9,12 @@ export const STORE: Readonly = { authnState: 'authnStateV1', countryState: 'countryStateV1', fulfillmentState: 'fulfillmentStateV1', + iamState: 'iamStateV1', invoiceState: 'invoiceStateV1', localeState: 'localeStateV1', orderState: 'orderStateV1', productState: 'productStateV1', + roleState: 'roleStateV1', routerState: 'routerStateV1', timezoneState: 'timezoneStateV1', }, diff --git a/packages/core/graphql/src/lib/documents/fragments/role.gql b/packages/core/graphql/src/lib/documents/fragments/role.gql new file mode 100644 index 00000000..877c43b5 --- /dev/null +++ b/packages/core/graphql/src/lib/documents/fragments/role.gql @@ -0,0 +1,6 @@ +fragment RoleFragment on IoRestorecommerceRoleRole { + id + name + description + assignableByRoles +} diff --git a/packages/core/graphql/src/lib/documents/fragments/timezone.gql b/packages/core/graphql/src/lib/documents/fragments/timezone.gql index 6a8f413d..309bcb9e 100644 --- a/packages/core/graphql/src/lib/documents/fragments/timezone.gql +++ b/packages/core/graphql/src/lib/documents/fragments/timezone.gql @@ -1,5 +1,6 @@ fragment TimezoneFragment on IoRestorecommerceTimezoneTimezone { id + value description meta { ...MetaFragment diff --git a/packages/core/graphql/src/lib/documents/fragments/user-role.gql b/packages/core/graphql/src/lib/documents/fragments/user-role.gql new file mode 100644 index 00000000..dbccb6d0 --- /dev/null +++ b/packages/core/graphql/src/lib/documents/fragments/user-role.gql @@ -0,0 +1,37 @@ +fragment UserRoleFragment on IoRestorecommerceUserUserRole { + id + active + activationCode + email + newEmail + name + firstName + lastName + userType + defaultScope + lastAccess + localeId + locale { + id + name + value + description + } + timezoneId + timezone { + id + name + value + description + } + roles { + ...RoleFragment + } + roleAssociations { + id + role + } + meta { + ...MetaFragment + } +} diff --git a/packages/core/graphql/src/lib/documents/identity/role-delete.gql b/packages/core/graphql/src/lib/documents/identity/role-delete.gql new file mode 100644 index 00000000..efaa7ad3 --- /dev/null +++ b/packages/core/graphql/src/lib/documents/identity/role-delete.gql @@ -0,0 +1,16 @@ +mutation IdentityRoleDelete( + $input: IIoRestorecommerceResourcebaseDeleteRequest! +) { + identity { + role { + Delete(input: $input) { + details { + operationStatus { + code + message + } + } + } + } + } +} diff --git a/packages/core/graphql/src/lib/documents/identity/role-mutate.gql b/packages/core/graphql/src/lib/documents/identity/role-mutate.gql new file mode 100644 index 00000000..57c55589 --- /dev/null +++ b/packages/core/graphql/src/lib/documents/identity/role-mutate.gql @@ -0,0 +1,19 @@ +mutation IdentityRoleMutate($input: IIoRestorecommerceRoleRoleList!) { + identity { + role { + Mutate(input: $input) { + details { + operationStatus { + code + message + } + items { + payload { + ...RoleFragment + } + } + } + } + } + } +} diff --git a/packages/core/graphql/src/lib/documents/identity/role-read.gql b/packages/core/graphql/src/lib/documents/identity/role-read.gql new file mode 100644 index 00000000..b94ba8ec --- /dev/null +++ b/packages/core/graphql/src/lib/documents/identity/role-read.gql @@ -0,0 +1,19 @@ +query IdentityRoleRead($input: IIoRestorecommerceResourcebaseReadRequest!) { + identity { + role { + Read(input: $input) { + details { + operationStatus { + code + message + } + items { + payload { + ...RoleFragment + } + } + } + } + } + } +} diff --git a/packages/core/graphql/src/lib/documents/identity/user-read.gql b/packages/core/graphql/src/lib/documents/identity/user-read.gql new file mode 100644 index 00000000..0b1c62c7 --- /dev/null +++ b/packages/core/graphql/src/lib/documents/identity/user-read.gql @@ -0,0 +1,19 @@ +query IdentityUserRead($input: IIoRestorecommerceResourcebaseReadRequest!) { + identity { + user { + Read(input: $input) { + details { + operationStatus { + code + message + } + items { + payload { + ...UserRoleFragment + } + } + } + } + } + } +} diff --git a/packages/core/graphql/src/lib/generated/generated.ts b/packages/core/graphql/src/lib/generated/generated.ts index 1a5a0703..9422548a 100644 --- a/packages/core/graphql/src/lib/generated/generated.ts +++ b/packages/core/graphql/src/lib/generated/generated.ts @@ -5460,6 +5460,7 @@ export type CatalogProductMutateMutation = { timezone?: { __typename?: 'IoRestorecommerceTimezoneTimezone'; id?: string | null; + value?: string | null; description?: string | null; meta?: { __typename?: 'IoRestorecommerceMetaMeta'; @@ -5628,6 +5629,7 @@ export type CatalogProductReadQuery = { timezone?: { __typename?: 'IoRestorecommerceTimezoneTimezone'; id?: string | null; + value?: string | null; description?: string | null; meta?: { __typename?: 'IoRestorecommerceMetaMeta'; @@ -5798,6 +5800,7 @@ export type ContactPointFragmentFragment = { timezone?: { __typename?: 'IoRestorecommerceTimezoneTimezone'; id?: string | null; + value?: string | null; description?: string | null; meta?: { __typename?: 'IoRestorecommerceMetaMeta'; @@ -5878,6 +5881,7 @@ export type CustomerFragmentFragment = { timezone?: { __typename?: 'IoRestorecommerceTimezoneTimezone'; id?: string | null; + value?: string | null; description?: string | null; meta?: { __typename?: 'IoRestorecommerceMetaMeta'; @@ -5953,6 +5957,7 @@ export type CustomerFragmentFragment = { timezone?: { __typename?: 'IoRestorecommerceTimezoneTimezone'; id?: string | null; + value?: string | null; description?: string | null; meta?: { __typename?: 'IoRestorecommerceMetaMeta'; @@ -6037,6 +6042,7 @@ export type CustomerFragmentFragment = { timezone?: { __typename?: 'IoRestorecommerceTimezoneTimezone'; id?: string | null; + value?: string | null; description?: string | null; meta?: { __typename?: 'IoRestorecommerceMetaMeta'; @@ -6224,6 +6230,7 @@ export type OrderFragmentFragment = { timezone?: { __typename?: 'IoRestorecommerceTimezoneTimezone'; id?: string | null; + value?: string | null; description?: string | null; meta?: { __typename?: 'IoRestorecommerceMetaMeta'; @@ -6372,6 +6379,7 @@ export type OrderFragmentFragment = { timezone?: { __typename?: 'IoRestorecommerceTimezoneTimezone'; id?: string | null; + value?: string | null; description?: string | null; meta?: { __typename?: 'IoRestorecommerceMetaMeta'; @@ -6457,6 +6465,7 @@ export type OrderFragmentFragment = { timezone?: { __typename?: 'IoRestorecommerceTimezoneTimezone'; id?: string | null; + value?: string | null; description?: string | null; meta?: { __typename?: 'IoRestorecommerceMetaMeta'; @@ -6532,6 +6541,7 @@ export type OrderFragmentFragment = { timezone?: { __typename?: 'IoRestorecommerceTimezoneTimezone'; id?: string | null; + value?: string | null; description?: string | null; meta?: { __typename?: 'IoRestorecommerceMetaMeta'; @@ -6616,6 +6626,7 @@ export type OrderFragmentFragment = { timezone?: { __typename?: 'IoRestorecommerceTimezoneTimezone'; id?: string | null; + value?: string | null; description?: string | null; meta?: { __typename?: 'IoRestorecommerceMetaMeta'; @@ -6787,6 +6798,7 @@ export type OrganizationFragmentFragment = { timezone?: { __typename?: 'IoRestorecommerceTimezoneTimezone'; id?: string | null; + value?: string | null; description?: string | null; meta?: { __typename?: 'IoRestorecommerceMetaMeta'; @@ -6871,6 +6883,7 @@ export type ProductFragmentFragment = { timezone?: { __typename?: 'IoRestorecommerceTimezoneTimezone'; id?: string | null; + value?: string | null; description?: string | null; meta?: { __typename?: 'IoRestorecommerceMetaMeta'; @@ -6973,6 +6986,14 @@ export type ProductFragmentFragment = { } | null; }; +export type RoleFragmentFragment = { + __typename?: 'IoRestorecommerceRoleRole'; + id?: string | null; + name?: string | null; + description?: string | null; + assignableByRoles?: Array | null; +}; + export type ShopFragmentFragment = { __typename?: 'IoRestorecommerceShopShop'; id?: string | null; @@ -7008,6 +7029,7 @@ export type ShopFragmentFragment = { timezone?: { __typename?: 'IoRestorecommerceTimezoneTimezone'; id?: string | null; + value?: string | null; description?: string | null; meta?: { __typename?: 'IoRestorecommerceMetaMeta'; @@ -7064,6 +7086,7 @@ export type ShopFragmentFragment = { export type TimezoneFragmentFragment = { __typename?: 'IoRestorecommerceTimezoneTimezone'; id?: string | null; + value?: string | null; description?: string | null; meta?: { __typename?: 'IoRestorecommerceMetaMeta'; @@ -7074,6 +7097,56 @@ export type TimezoneFragmentFragment = { } | null; }; +export type UserRoleFragmentFragment = { + __typename?: 'IoRestorecommerceUserUserRole'; + id?: string | null; + active?: boolean | null; + activationCode?: string | null; + email?: string | null; + newEmail?: string | null; + name?: string | null; + firstName?: string | null; + lastName?: string | null; + userType?: IoRestorecommerceUserUserType | null; + defaultScope?: string | null; + lastAccess?: unknown | null; + localeId?: string | null; + timezoneId?: string | null; + locale?: { + __typename?: 'IoRestorecommerceLocaleLocale'; + id?: string | null; + name?: string | null; + value?: string | null; + description?: string | null; + } | null; + timezone?: { + __typename?: 'IoRestorecommerceTimezoneTimezone'; + id?: string | null; + name?: string | null; + value?: string | null; + description?: string | null; + } | null; + roles?: Array<{ + __typename?: 'IoRestorecommerceRoleRole'; + id?: string | null; + name?: string | null; + description?: string | null; + assignableByRoles?: Array | null; + }> | null; + roleAssociations?: Array<{ + __typename?: 'IoRestorecommerceAuthRoleAssociation'; + id?: string | null; + role?: string | null; + }> | null; + meta?: { + __typename?: 'IoRestorecommerceMetaMeta'; + created?: unknown | null; + modified?: unknown | null; + createdBy?: string | null; + modifiedBy?: string | null; + } | null; +}; + export type UserFragmentFragment = { __typename?: 'IoRestorecommerceUserUser'; id?: string | null; @@ -7210,6 +7283,101 @@ export type FulfillmentFulfillmentReadQuery = { }; }; +export type IdentityRoleDeleteMutationVariables = Exact<{ + input: IIoRestorecommerceResourcebaseDeleteRequest; +}>; + +export type IdentityRoleDeleteMutation = { + __typename?: 'Mutation'; + identity: { + __typename?: 'IdentityMutation'; + role: { + __typename?: 'IdentityRoleMutation'; + Delete?: { + __typename?: 'ProtoIoRestorecommerceResourcebaseDeleteResponse'; + details?: { + __typename?: 'IoRestorecommerceResourcebaseDeleteResponse'; + operationStatus?: { + __typename?: 'IoRestorecommerceStatusOperationStatus'; + code?: number | null; + message?: string | null; + } | null; + } | null; + } | null; + }; + }; +}; + +export type IdentityRoleMutateMutationVariables = Exact<{ + input: IIoRestorecommerceRoleRoleList; +}>; + +export type IdentityRoleMutateMutation = { + __typename?: 'Mutation'; + identity: { + __typename?: 'IdentityMutation'; + role: { + __typename?: 'IdentityRoleMutation'; + Mutate?: { + __typename?: 'ProtoIoRestorecommerceRoleRoleListResponse'; + details?: { + __typename?: 'IoRestorecommerceRoleRoleListResponse'; + operationStatus?: { + __typename?: 'IoRestorecommerceStatusOperationStatus'; + code?: number | null; + message?: string | null; + } | null; + items?: Array<{ + __typename?: 'IoRestorecommerceRoleRoleResponse'; + payload?: { + __typename?: 'IoRestorecommerceRoleRole'; + id?: string | null; + name?: string | null; + description?: string | null; + assignableByRoles?: Array | null; + } | null; + }> | null; + } | null; + } | null; + }; + }; +}; + +export type IdentityRoleReadQueryVariables = Exact<{ + input: IIoRestorecommerceResourcebaseReadRequest; +}>; + +export type IdentityRoleReadQuery = { + __typename?: 'Query'; + identity: { + __typename?: 'IdentityQuery'; + role: { + __typename?: 'IdentityRoleQuery'; + Read?: { + __typename?: 'ProtoIoRestorecommerceRoleRoleListResponse'; + details?: { + __typename?: 'IoRestorecommerceRoleRoleListResponse'; + operationStatus?: { + __typename?: 'IoRestorecommerceStatusOperationStatus'; + code?: number | null; + message?: string | null; + } | null; + items?: Array<{ + __typename?: 'IoRestorecommerceRoleRoleResponse'; + payload?: { + __typename?: 'IoRestorecommerceRoleRole'; + id?: string | null; + name?: string | null; + description?: string | null; + assignableByRoles?: Array | null; + } | null; + }> | null; + } | null; + } | null; + }; + }; +}; + export type IdentityUserActivateMutationVariables = Exact<{ input: IIoRestorecommerceUserActivateRequest; }>; @@ -7494,6 +7662,83 @@ export type IdentityUserMutateMutation = { }; }; +export type IdentityUserReadQueryVariables = Exact<{ + input: IIoRestorecommerceResourcebaseReadRequest; +}>; + +export type IdentityUserReadQuery = { + __typename?: 'Query'; + identity: { + __typename?: 'IdentityQuery'; + user: { + __typename?: 'IdentityUserQuery'; + Read?: { + __typename?: 'ProtoIoRestorecommerceUserUserListWithRoleResponse'; + details?: { + __typename?: 'IoRestorecommerceUserUserListWithRoleResponse'; + operationStatus?: { + __typename?: 'IoRestorecommerceStatusOperationStatus'; + code?: number | null; + message?: string | null; + } | null; + items?: Array<{ + __typename?: 'IoRestorecommerceUserUserRoleResponse'; + payload?: { + __typename?: 'IoRestorecommerceUserUserRole'; + id?: string | null; + active?: boolean | null; + activationCode?: string | null; + email?: string | null; + newEmail?: string | null; + name?: string | null; + firstName?: string | null; + lastName?: string | null; + userType?: IoRestorecommerceUserUserType | null; + defaultScope?: string | null; + lastAccess?: unknown | null; + localeId?: string | null; + timezoneId?: string | null; + locale?: { + __typename?: 'IoRestorecommerceLocaleLocale'; + id?: string | null; + name?: string | null; + value?: string | null; + description?: string | null; + } | null; + timezone?: { + __typename?: 'IoRestorecommerceTimezoneTimezone'; + id?: string | null; + name?: string | null; + value?: string | null; + description?: string | null; + } | null; + roles?: Array<{ + __typename?: 'IoRestorecommerceRoleRole'; + id?: string | null; + name?: string | null; + description?: string | null; + assignableByRoles?: Array | null; + }> | null; + roleAssociations?: Array<{ + __typename?: 'IoRestorecommerceAuthRoleAssociation'; + id?: string | null; + role?: string | null; + }> | null; + meta?: { + __typename?: 'IoRestorecommerceMetaMeta'; + created?: unknown | null; + modified?: unknown | null; + createdBy?: string | null; + modifiedBy?: string | null; + } | null; + } | null; + }> | null; + } | null; + } | null; + }; + }; +}; + export type IdentityUserRegisterMutationVariables = Exact<{ input: IIoRestorecommerceUserRegisterRequest; }>; @@ -8037,6 +8282,7 @@ export type MasterDataTimezoneReadQuery = { payload?: { __typename?: 'IoRestorecommerceTimezoneTimezone'; id?: string | null; + value?: string | null; description?: string | null; meta?: { __typename?: 'IoRestorecommerceMetaMeta'; @@ -8154,6 +8400,7 @@ export type OrderingOrderMutateMutation = { timezone?: { __typename?: 'IoRestorecommerceTimezoneTimezone'; id?: string | null; + value?: string | null; description?: string | null; meta?: { __typename?: 'IoRestorecommerceMetaMeta'; @@ -8302,6 +8549,7 @@ export type OrderingOrderMutateMutation = { timezone?: { __typename?: 'IoRestorecommerceTimezoneTimezone'; id?: string | null; + value?: string | null; description?: string | null; meta?: { __typename?: 'IoRestorecommerceMetaMeta'; @@ -8387,6 +8635,7 @@ export type OrderingOrderMutateMutation = { timezone?: { __typename?: 'IoRestorecommerceTimezoneTimezone'; id?: string | null; + value?: string | null; description?: string | null; meta?: { __typename?: 'IoRestorecommerceMetaMeta'; @@ -8462,6 +8711,7 @@ export type OrderingOrderMutateMutation = { timezone?: { __typename?: 'IoRestorecommerceTimezoneTimezone'; id?: string | null; + value?: string | null; description?: string | null; meta?: { __typename?: 'IoRestorecommerceMetaMeta'; @@ -8546,6 +8796,7 @@ export type OrderingOrderMutateMutation = { timezone?: { __typename?: 'IoRestorecommerceTimezoneTimezone'; id?: string | null; + value?: string | null; description?: string | null; meta?: { __typename?: 'IoRestorecommerceMetaMeta'; @@ -8765,6 +9016,7 @@ export type OrderingOrderReadQuery = { timezone?: { __typename?: 'IoRestorecommerceTimezoneTimezone'; id?: string | null; + value?: string | null; description?: string | null; meta?: { __typename?: 'IoRestorecommerceMetaMeta'; @@ -8913,6 +9165,7 @@ export type OrderingOrderReadQuery = { timezone?: { __typename?: 'IoRestorecommerceTimezoneTimezone'; id?: string | null; + value?: string | null; description?: string | null; meta?: { __typename?: 'IoRestorecommerceMetaMeta'; @@ -8998,6 +9251,7 @@ export type OrderingOrderReadQuery = { timezone?: { __typename?: 'IoRestorecommerceTimezoneTimezone'; id?: string | null; + value?: string | null; description?: string | null; meta?: { __typename?: 'IoRestorecommerceMetaMeta'; @@ -9073,6 +9327,7 @@ export type OrderingOrderReadQuery = { timezone?: { __typename?: 'IoRestorecommerceTimezoneTimezone'; id?: string | null; + value?: string | null; description?: string | null; meta?: { __typename?: 'IoRestorecommerceMetaMeta'; @@ -9157,6 +9412,7 @@ export type OrderingOrderReadQuery = { timezone?: { __typename?: 'IoRestorecommerceTimezoneTimezone'; id?: string | null; + value?: string | null; description?: string | null; meta?: { __typename?: 'IoRestorecommerceMetaMeta'; @@ -9433,6 +9689,7 @@ export const LocationFragmentFragmentDoc = gql` export const TimezoneFragmentFragmentDoc = gql` fragment TimezoneFragment on IoRestorecommerceTimezoneTimezone { id + value description meta { ...MetaFragment @@ -9720,6 +9977,55 @@ export const OrderFragmentFragmentDoc = gql` ${UserFragmentFragmentDoc} ${MetaFragmentFragmentDoc} `; +export const RoleFragmentFragmentDoc = gql` + fragment RoleFragment on IoRestorecommerceRoleRole { + id + name + description + assignableByRoles + } +`; +export const UserRoleFragmentFragmentDoc = gql` + fragment UserRoleFragment on IoRestorecommerceUserUserRole { + id + active + activationCode + email + newEmail + name + firstName + lastName + userType + defaultScope + lastAccess + localeId + locale { + id + name + value + description + } + timezoneId + timezone { + id + name + value + description + } + roles { + ...RoleFragment + } + roleAssociations { + id + role + } + meta { + ...MetaFragment + } + } + ${RoleFragmentFragmentDoc} + ${MetaFragmentFragmentDoc} +`; export const CatalogProductDeleteDocument = gql` mutation CatalogProductDelete( $input: IIoRestorecommerceResourcebaseDeleteRequest! @@ -9932,6 +10238,110 @@ export class FulfillmentFulfillmentReadGQL extends Apollo.Query< super(apollo); } } +export const IdentityRoleDeleteDocument = gql` + mutation IdentityRoleDelete( + $input: IIoRestorecommerceResourcebaseDeleteRequest! + ) { + identity { + role { + Delete(input: $input) { + details { + operationStatus { + code + message + } + } + } + } + } + } +`; + +@Injectable({ + providedIn: 'root', +}) +export class IdentityRoleDeleteGQL extends Apollo.Mutation< + IdentityRoleDeleteMutation, + IdentityRoleDeleteMutationVariables +> { + override document = IdentityRoleDeleteDocument; + + constructor(apollo: Apollo.Apollo) { + super(apollo); + } +} +export const IdentityRoleMutateDocument = gql` + mutation IdentityRoleMutate($input: IIoRestorecommerceRoleRoleList!) { + identity { + role { + Mutate(input: $input) { + details { + operationStatus { + code + message + } + items { + payload { + ...RoleFragment + } + } + } + } + } + } + } + ${RoleFragmentFragmentDoc} +`; + +@Injectable({ + providedIn: 'root', +}) +export class IdentityRoleMutateGQL extends Apollo.Mutation< + IdentityRoleMutateMutation, + IdentityRoleMutateMutationVariables +> { + override document = IdentityRoleMutateDocument; + + constructor(apollo: Apollo.Apollo) { + super(apollo); + } +} +export const IdentityRoleReadDocument = gql` + query IdentityRoleRead($input: IIoRestorecommerceResourcebaseReadRequest!) { + identity { + role { + Read(input: $input) { + details { + operationStatus { + code + message + } + items { + payload { + ...RoleFragment + } + } + } + } + } + } + } + ${RoleFragmentFragmentDoc} +`; + +@Injectable({ + providedIn: 'root', +}) +export class IdentityRoleReadGQL extends Apollo.Query< + IdentityRoleReadQuery, + IdentityRoleReadQueryVariables +> { + override document = IdentityRoleReadDocument; + + constructor(apollo: Apollo.Apollo) { + super(apollo); + } +} export const IdentityUserActivateDocument = gql` mutation IdentityUserActivate( $input: IIoRestorecommerceUserActivateRequest! @@ -10200,6 +10610,42 @@ export class IdentityUserMutateGQL extends Apollo.Mutation< super(apollo); } } +export const IdentityUserReadDocument = gql` + query IdentityUserRead($input: IIoRestorecommerceResourcebaseReadRequest!) { + identity { + user { + Read(input: $input) { + details { + operationStatus { + code + message + } + items { + payload { + ...UserRoleFragment + } + } + } + } + } + } + } + ${UserRoleFragmentFragmentDoc} +`; + +@Injectable({ + providedIn: 'root', +}) +export class IdentityUserReadGQL extends Apollo.Query< + IdentityUserReadQuery, + IdentityUserReadQueryVariables +> { + override document = IdentityUserReadDocument; + + constructor(apollo: Apollo.Apollo) { + super(apollo); + } +} export const IdentityUserRegisterDocument = gql` mutation IdentityUserRegister( $input: IIoRestorecommerceUserRegisterRequest! diff --git a/packages/core/state/src/lib/+state/account/account.effects.ts b/packages/core/state/src/lib/+state/account/account.effects.ts index 8036f7e8..32d514fa 100644 --- a/packages/core/state/src/lib/+state/account/account.effects.ts +++ b/packages/core/state/src/lib/+state/account/account.effects.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { Actions, createEffect, ofType } from '@ngrx/effects'; import { of } from 'rxjs'; -import { catchError, map, switchMap, tap } from 'rxjs/operators'; +import { catchError, exhaustMap, map, switchMap, tap } from 'rxjs/operators'; import { ENotificationTypes, @@ -21,8 +21,8 @@ export class AccountEffects { userFindRequest$ = createEffect(() => { return this.actions$.pipe( ofType(accountActions.userFindRequest), - switchMap(({ payload }) => - this.accountService.find(payload).pipe( + exhaustMap(({ payload }) => + this.userService.find(payload).pipe( map((result) => { const identity = result?.data?.identity; const operationStatus = @@ -36,7 +36,7 @@ export class AccountEffects { }), map((payload) => { return accountActions.userFindSuccess({ - payload, + payload: this.userService.getUserWithRolesAndFullName(payload), }); }), catchError((error: Error) => @@ -51,7 +51,7 @@ export class AccountEffects { return this.actions$.pipe( ofType(accountActions.userFindByTokenRequest), switchMap(({ payload }) => - this.accountService.findByToken(payload).pipe( + this.userService.findByToken(payload).pipe( tap((result) => { const identity = result?.data?.identity; const status = identity?.user?.FindByToken?.details?.status; @@ -72,7 +72,9 @@ export class AccountEffects { map((result) => { const payload = result?.data?.identity?.user?.FindByToken?.details ?.payload as IUser; - return accountActions.userFindByTokenSuccess({ payload }); + return accountActions.userFindByTokenSuccess({ + payload: this.userService.getUserWithRolesAndFullName(payload), + }); }), catchError((error: Error) => of(accountActions.userFindByTokenFail({ error: error.message })) @@ -86,7 +88,7 @@ export class AccountEffects { return this.actions$.pipe( ofType(accountActions.userMutateRequest), switchMap(({ payload }) => - this.accountService.mutate(payload).pipe( + this.userService.mutate(payload).pipe( map((result) => { const identity = result?.data?.identity; const operationStatus = @@ -99,7 +101,9 @@ export class AccountEffects { return payload; }), map((payload) => { - return accountActions.userMutateSuccess({ payload }); + return accountActions.userMutateSuccess({ + payload: this.userService.getUserWithRolesAndFullName(payload), + }); }), catchError((error: Error) => of(accountActions.userMutateFail({ error: error.message })) @@ -128,7 +132,7 @@ export class AccountEffects { return this.actions$.pipe( ofType(accountActions.userChangeEmailRequest), switchMap(({ payload }) => - this.accountService.requestEmailChange(payload).pipe( + this.userService.requestEmailChange(payload).pipe( tap((result) => { this.errorHandlingService.checkStatusAndThrow( result?.data?.identity?.user?.RequestEmailChange?.details @@ -165,7 +169,7 @@ export class AccountEffects { return this.actions$.pipe( ofType(accountActions.userConfirmEmailChangeRequest), switchMap(({ payload }) => - this.accountService.confirmEmailChange(payload).pipe( + this.userService.confirmEmailChange(payload).pipe( tap((result) => { this.errorHandlingService.checkStatusAndThrow( result?.data?.identity?.user?.ConfirmEmailChange?.details @@ -209,7 +213,7 @@ export class AccountEffects { return this.actions$.pipe( ofType(accountActions.userChangePasswordRequest), switchMap(({ payload }) => - this.accountService.passwordChange(payload).pipe( + this.userService.passwordChange(payload).pipe( tap((result) => { this.errorHandlingService.checkStatusAndThrow( result?.data?.identity?.user?.ChangePassword?.details @@ -246,7 +250,7 @@ export class AccountEffects { return this.actions$.pipe( ofType(accountActions.userRemoveRequest), switchMap(({ payload }) => - this.accountService.remove(payload).pipe( + this.userService.remove(payload).pipe( tap((result) => { this.errorHandlingService.checkStatusAndThrow( result?.data?.identity?.user?.Delete?.details @@ -316,7 +320,7 @@ export class AccountEffects { private readonly actions$: Actions, private readonly appFacade: AppFacade, private readonly authnFacade: AuthnFacade, - private readonly accountService: UserService, + private readonly userService: UserService, private readonly errorHandlingService: ErrorHandlingService ) {} } diff --git a/packages/core/state/src/lib/+state/account/account.reducer.ts b/packages/core/state/src/lib/+state/account/account.reducer.ts index 0d6c8c1e..a912a3cb 100644 --- a/packages/core/state/src/lib/+state/account/account.reducer.ts +++ b/packages/core/state/src/lib/+state/account/account.reducer.ts @@ -2,8 +2,6 @@ import { Action, createReducer, on } from '@ngrx/store'; import { EActionStatus, IAccountState } from '@console-core/types'; -import { getUser } from '../../utils'; - import * as accountActions from './account.actions'; export const initialState: IAccountState = { @@ -26,7 +24,7 @@ const reducer = createReducer( accountActions.userFindSuccess, (state, { payload }): IAccountState => ({ ...state, - user: getUser(payload), + user: payload, actionStatus: EActionStatus.Succeeded, error: null, }) @@ -51,7 +49,7 @@ const reducer = createReducer( accountActions.userFindByTokenSuccess, (state, { payload }): IAccountState => ({ ...state, - user: getUser(payload), + user: payload, actionStatus: EActionStatus.Succeeded, error: null, }) @@ -76,7 +74,7 @@ const reducer = createReducer( accountActions.userMutateSuccess, (state, { payload }): IAccountState => ({ ...state, - user: getUser(payload), + user: payload, actionStatus: EActionStatus.Succeeded, error: null, }) diff --git a/packages/core/state/src/lib/+state/management/access-control/index.ts b/packages/core/state/src/lib/+state/management/access-control/index.ts new file mode 100644 index 00000000..efbebd06 --- /dev/null +++ b/packages/core/state/src/lib/+state/management/access-control/index.ts @@ -0,0 +1 @@ +export * from './role'; diff --git a/packages/core/state/src/lib/+state/management/access-control/role/index.ts b/packages/core/state/src/lib/+state/management/access-control/role/index.ts new file mode 100644 index 00000000..cb7f368f --- /dev/null +++ b/packages/core/state/src/lib/+state/management/access-control/role/index.ts @@ -0,0 +1,3 @@ +export { RoleEffects } from './role.effects'; +export { RoleFacade } from './role.facade'; +export { roleReducer } from './role.reducer'; diff --git a/packages/core/state/src/lib/+state/management/access-control/role/role.actions.ts b/packages/core/state/src/lib/+state/management/access-control/role/role.actions.ts new file mode 100644 index 00000000..586385d1 --- /dev/null +++ b/packages/core/state/src/lib/+state/management/access-control/role/role.actions.ts @@ -0,0 +1,87 @@ +import { createAction, props } from '@ngrx/store'; + +import { + IIoRestorecommerceRoleRoleList, + IIoRestorecommerceResourcebaseReadRequest, +} from '@console-core/graphql'; +import { IRole } from '@console-core/types'; + +export const roleReadRequest = createAction( + '[ROLE] Read request', + props<{ payload: IIoRestorecommerceResourcebaseReadRequest }>() +); + +export const roleReadRequestSuccess = createAction( + '[ROLE] Read success', + props<{ payload: IRole[] }>() +); + +export const roleReadRequestFail = createAction( + '[ROLE] Read fail', + props<{ error: string }>() +); + +export const roleReadOneByIdRequest = createAction( + '[ROLE] Read one by id request', + props<{ payload: { id: string } }>() +); + +export const roleReadOneByIdRequestSuccess = createAction( + '[ROLE] Read one by id success', + props<{ payload: IRole }>() +); + +export const roleReadOneByIdRequestFail = createAction( + '[ROLE] Read one by id fail', + props<{ error: string }>() +); + +export const setSelectedId = createAction( + '[ROLE] Set selected id', + props<{ payload: string | null }>() +); + +export const roleCreateRequest = createAction( + '[ROLE] Role create request', + props<{ payload: IIoRestorecommerceRoleRoleList }>() +); + +export const roleCreateSuccess = createAction( + '[ROLE] Role create success', + props<{ payload: IRole }>() +); + +export const roleCreateFail = createAction( + '[ROLE] Role create fail', + props<{ error: string }>() +); + +export const roleUpdateRequest = createAction( + '[ROLE] Role update request', + props<{ payload: IIoRestorecommerceRoleRoleList }>() +); + +export const roleUpdateSuccess = createAction( + '[ROLE] Role update success', + props<{ payload: IRole }>() +); + +export const roleUpdateFail = createAction( + '[ROLE] Role update fail', + props<{ error: string }>() +); + +export const roleRemoveRequest = createAction( + '[ROLE] Role remove request', + props<{ payload: { id: string } }>() +); + +export const roleRemoveSuccess = createAction( + '[ROLE] Role remove success', + props<{ payload: { id: string } }>() +); + +export const roleRemoveFail = createAction( + '[ROLE] Role remove fail', + props<{ error: string }>() +); diff --git a/packages/core/state/src/lib/+state/management/access-control/role/role.effects.ts b/packages/core/state/src/lib/+state/management/access-control/role/role.effects.ts new file mode 100644 index 00000000..de842d6e --- /dev/null +++ b/packages/core/state/src/lib/+state/management/access-control/role/role.effects.ts @@ -0,0 +1,252 @@ +import { Injectable } from '@angular/core'; +import { Router } from '@angular/router'; +import { Actions, createEffect, ofType } from '@ngrx/effects'; +import { of } from 'rxjs'; +import { catchError, exhaustMap, map, switchMap, tap } from 'rxjs/operators'; + +import { ROUTER } from '@console-core/config'; +import { + IoRestorecommerceResourcebaseFilterOperation, + IoRestorecommerceResourcebaseFilterValueType, +} from '@console-core/graphql'; +import { + ENotificationTypes, + IRole, + TOperationStatus, +} from '@console-core/types'; + +import { RoleService, ErrorHandlingService } from '../../../../services'; +import { AppFacade } from '../../../app'; + +import * as roleActions from './role.actions'; + +@Injectable() +export class RoleEffects { + roleReadRequest$ = createEffect(() => { + return this.actions$.pipe( + ofType(roleActions.roleReadRequest), + exhaustMap(({ payload }) => + this.roleService.read(payload).pipe( + tap((result) => { + this.errorHandlingService.checkStatusAndThrow( + result?.data?.identity?.role?.Read?.details + ?.operationStatus as TOperationStatus + ); + }), + map((result) => { + const payload = ( + result?.data?.identity?.role?.Read?.details?.items || [] + )?.map((item) => item?.payload) as IRole[]; + return roleActions.roleReadRequestSuccess({ payload }); + }), + catchError((error: Error) => + of(roleActions.roleReadRequestFail({ error: error.message })) + ) + ) + ) + ); + }); + + roleReadOneByIdRequest$ = createEffect(() => { + return this.actions$.pipe( + ofType(roleActions.roleReadOneByIdRequest), + exhaustMap(({ payload }) => + this.roleService + .read({ + filters: [ + { + filters: [ + { + field: 'id', + value: payload.id, + type: IoRestorecommerceResourcebaseFilterValueType.String, + operation: IoRestorecommerceResourcebaseFilterOperation.Eq, + }, + ], + }, + ], + limit: 1, + }) + .pipe( + tap((result) => { + this.errorHandlingService.checkStatusAndThrow( + result?.data?.identity?.role?.Read?.details + ?.operationStatus as TOperationStatus + ); + }), + map((result) => { + const payload = + result?.data?.identity?.role?.Read?.details?.items?.pop() + ?.payload as IRole; + return roleActions.roleReadOneByIdRequestSuccess({ + payload, + }); + }), + catchError((error: Error) => + of( + roleActions.roleReadOneByIdRequestFail({ + error: error.message, + }) + ) + ) + ) + ) + ); + }); + + roleCreateRequest$ = createEffect(() => { + return this.actions$.pipe( + ofType(roleActions.roleCreateRequest), + switchMap(({ payload }) => + this.roleService.mutate(payload).pipe( + tap((result) => { + this.errorHandlingService.checkStatusAndThrow( + result?.data?.identity?.role?.Mutate?.details + ?.operationStatus as TOperationStatus + ); + }), + map((result) => { + const payload = + result?.data?.identity?.role?.Mutate?.details?.items?.pop() + ?.payload as IRole; + return roleActions.roleCreateSuccess({ payload }); + }), + catchError((error: Error) => + of(roleActions.roleCreateFail({ error: error.message })) + ) + ) + ) + ); + }); + + roleCreateSuccess$ = createEffect( + () => { + return this.actions$.pipe( + ofType(roleActions.roleCreateSuccess), + tap(() => { + this.appFacade.addNotification({ + content: 'role created', + type: ENotificationTypes.Success, + }); + }), + tap(({ payload }) => { + this.router.navigate( + ROUTER.pages.main.children.management.children.accessControl.children.roles.children.edit.getLink( + { id: payload.id } + ) + ); + }) + ); + }, + { dispatch: false } + ); + + roleUpdateRequest$ = createEffect(() => { + return this.actions$.pipe( + ofType(roleActions.roleUpdateRequest), + switchMap(({ payload }) => + this.roleService.mutate(payload).pipe( + tap((result) => { + this.errorHandlingService.checkStatusAndThrow( + result?.data?.identity?.role?.Mutate?.details + ?.operationStatus as TOperationStatus + ); + }), + map((result) => { + const payload = + result?.data?.identity?.role?.Mutate?.details?.items?.pop() + ?.payload as IRole; + return roleActions.roleUpdateSuccess({ payload }); + }), + catchError((error: Error) => + of(roleActions.roleUpdateFail({ error: error.message })) + ) + ) + ) + ); + }); + + roleUpdateSuccess$ = createEffect( + () => { + return this.actions$.pipe( + ofType(roleActions.roleUpdateSuccess), + tap(() => { + this.appFacade.addNotification({ + content: 'role updated', + type: ENotificationTypes.Success, + }); + }) + ); + }, + { dispatch: false } + ); + + roleRemoveRequest$ = createEffect(() => { + return this.actions$.pipe( + ofType(roleActions.roleRemoveRequest), + switchMap(({ payload }) => { + const id = payload.id; + return this.roleService.remove({ ids: [id] }).pipe( + tap((result) => { + this.errorHandlingService.checkStatusAndThrow( + result?.data?.identity?.role?.Delete?.details + ?.operationStatus as TOperationStatus + ); + }), + map(() => { + return roleActions.roleRemoveSuccess({ + payload: { id }, + }); + }), + catchError((error: Error) => + of(roleActions.roleRemoveFail({ error: error.message })) + ) + ); + }) + ); + }); + + roleRemoveSuccess$ = createEffect( + () => { + return this.actions$.pipe( + ofType(roleActions.roleRemoveSuccess), + tap(() => { + this.appFacade.addNotification({ + content: 'role deleted', + type: ENotificationTypes.Success, + }); + }) + ); + }, + { dispatch: false } + ); + + handleNotificationErrors$ = createEffect( + () => { + return this.actions$.pipe( + ofType( + roleActions.roleReadRequestFail, + roleActions.roleReadOneByIdRequestFail, + roleActions.roleCreateFail, + roleActions.roleUpdateFail, + roleActions.roleRemoveFail + ), + tap(({ error }) => { + this.appFacade.addNotification({ + content: error ?? 'unknown error', + type: ENotificationTypes.Error, + }); + }) + ); + }, + { dispatch: false } + ); + + constructor( + private readonly router: Router, + private readonly actions$: Actions, + private readonly appFacade: AppFacade, + private readonly roleService: RoleService, + private readonly errorHandlingService: ErrorHandlingService + ) {} +} diff --git a/packages/core/state/src/lib/+state/management/access-control/role/role.facade.ts b/packages/core/state/src/lib/+state/management/access-control/role/role.facade.ts new file mode 100644 index 00000000..531406d7 --- /dev/null +++ b/packages/core/state/src/lib/+state/management/access-control/role/role.facade.ts @@ -0,0 +1,42 @@ +import { Injectable } from '@angular/core'; +import { Store } from '@ngrx/store'; + +import { + IIoRestorecommerceRoleRoleList, + IIoRestorecommerceResourcebaseReadRequest, +} from '@console-core/graphql'; + +import * as roleActions from './role.actions'; +import * as roleSelectors from './role.selectors'; + +@Injectable() +export class RoleFacade { + // Selectors + readonly ids$ = this.store.select(roleSelectors.selectRoleIds); + readonly entities$ = this.store.select(roleSelectors.selectRoleEntities); + readonly all$ = this.store.select(roleSelectors.selectRoleAll); + readonly allDistinctAssignableByRoles$ = this.store.select( + roleSelectors.selectRoleAllDistinctAssignableByRoles + ); + readonly total$ = this.store.select(roleSelectors.selectRoleTotal); + readonly selectedId$ = this.store.select(roleSelectors.selectRoleSelectedId); + readonly selected$ = this.store.select(roleSelectors.selectRoleSelected); + readonly actionStatus$ = this.store.select(roleSelectors.selectActionStatus); + readonly error$ = this.store.select(roleSelectors.selectError); + + // Actions + read = (payload: IIoRestorecommerceResourcebaseReadRequest) => + this.store.dispatch(roleActions.roleReadRequest({ payload })); + readOneById = (payload: { id: string }) => + this.store.dispatch(roleActions.roleReadOneByIdRequest({ payload })); + setSelectedId = (payload: string | null) => + this.store.dispatch(roleActions.setSelectedId({ payload })); + create = (payload: IIoRestorecommerceRoleRoleList) => + this.store.dispatch(roleActions.roleCreateRequest({ payload })); + update = (payload: IIoRestorecommerceRoleRoleList) => + this.store.dispatch(roleActions.roleUpdateRequest({ payload })); + remove = (payload: { id: string }) => + this.store.dispatch(roleActions.roleRemoveRequest({ payload })); + + constructor(private readonly store: Store) {} +} diff --git a/packages/core/state/src/lib/+state/management/access-control/role/role.reducer.ts b/packages/core/state/src/lib/+state/management/access-control/role/role.reducer.ts new file mode 100644 index 00000000..3c622ce1 --- /dev/null +++ b/packages/core/state/src/lib/+state/management/access-control/role/role.reducer.ts @@ -0,0 +1,149 @@ +import { EntityAdapter, createEntityAdapter } from '@ngrx/entity'; +import { Action, createReducer, on } from '@ngrx/store'; + +import { EActionStatus, IRole, IRoleState } from '@console-core/types'; + +import * as roleActions from './role.actions'; + +export const adapter: EntityAdapter = createEntityAdapter(); + +export const initialState: IRoleState = adapter.getInitialState({ + selectedId: null, + actionStatus: EActionStatus.INIT, + error: null, +}); + +const reducer = createReducer( + initialState, + on( + roleActions.roleReadRequest, + (state): IRoleState => ({ + ...state, + actionStatus: EActionStatus.Requesting, + }) + ), + on( + roleActions.roleReadRequestSuccess, + (state, { payload }): IRoleState => + adapter.setAll(payload, { + ...state, + actionStatus: EActionStatus.Succeeded, + }) + ), + on( + roleActions.roleReadRequestFail, + (state, { error }): IRoleState => ({ + ...state, + actionStatus: EActionStatus.Failed, + error, + }) + ), + on( + roleActions.roleReadOneByIdRequest, + (state): IRoleState => ({ + ...state, + actionStatus: EActionStatus.Requesting, + }) + ), + on( + roleActions.roleReadOneByIdRequestSuccess, + (state, { payload }): IRoleState => + adapter.updateOne( + { id: payload.id, changes: payload }, + { + ...state, + actionStatus: EActionStatus.Succeeded, + } + ) + ), + on( + roleActions.roleReadOneByIdRequestFail, + (state, { error }): IRoleState => ({ + ...state, + actionStatus: EActionStatus.Failed, + error, + }) + ), + on( + roleActions.setSelectedId, + (state, { payload }): IRoleState => ({ + ...state, + selectedId: payload, + }) + ), + on( + roleActions.roleCreateRequest, + (state): IRoleState => ({ + ...state, + actionStatus: EActionStatus.Mutating, + }) + ), + on( + roleActions.roleCreateSuccess, + (state, { payload }): IRoleState => + adapter.addOne(payload, { + ...state, + actionStatus: EActionStatus.Succeeded, + }) + ), + on( + roleActions.roleCreateFail, + (state, { error }): IRoleState => ({ + ...state, + actionStatus: EActionStatus.Failed, + error, + }) + ), + on( + roleActions.roleUpdateRequest, + (state): IRoleState => ({ + ...state, + actionStatus: EActionStatus.Mutating, + }) + ), + on( + roleActions.roleUpdateSuccess, + (state, { payload }): IRoleState => + adapter.updateOne( + { id: payload.id, changes: payload }, + { + ...state, + actionStatus: EActionStatus.Succeeded, + } + ) + ), + on( + roleActions.roleUpdateFail, + (state, { error }): IRoleState => ({ + ...state, + actionStatus: EActionStatus.Failed, + error, + }) + ), + on( + roleActions.roleRemoveRequest, + (state): IRoleState => ({ + ...state, + actionStatus: EActionStatus.Mutating, + }) + ), + on( + roleActions.roleRemoveSuccess, + (state, { payload }): IRoleState => + adapter.removeOne(payload.id, { + ...state, + actionStatus: EActionStatus.Succeeded, + }) + ), + on( + roleActions.roleRemoveFail, + (state, { error }): IRoleState => ({ + ...state, + actionStatus: EActionStatus.Failed, + error, + }) + ) +); + +export const roleReducer = (state: IRoleState | undefined, action: Action) => + reducer(state, action); diff --git a/packages/core/state/src/lib/+state/management/access-control/role/role.selectors.ts b/packages/core/state/src/lib/+state/management/access-control/role/role.selectors.ts new file mode 100644 index 00000000..4e32f47c --- /dev/null +++ b/packages/core/state/src/lib/+state/management/access-control/role/role.selectors.ts @@ -0,0 +1,56 @@ +import { createFeatureSelector, createSelector } from '@ngrx/store'; + +import { STORE } from '@console-core/config'; +import { IRole, IRoleState } from '@console-core/types'; + +import { adapter } from './role.reducer'; + +export const selectRole = createFeatureSelector( + STORE.states.roleState +); + +const { selectIds, selectEntities, selectAll, selectTotal } = + adapter.getSelectors(); + +export const selectRoleIds = createSelector(selectRole, selectIds); + +export const selectRoleEntities = createSelector(selectRole, selectEntities); + +export const selectRoleAll = createSelector(selectRole, selectAll); + +export const selectRoleTotal = createSelector(selectRole, selectTotal); + +export const selectRoleAllDistinctAssignableByRoles = createSelector( + selectRoleAll, + (roles) => { + const assignableByRoles = roles + .map((role) => role.assignableByRoles || []) + .flat() as string[]; + return Array.from(new Set(assignableByRoles)); + } +); + +export const selectRoleSelectedId = createSelector( + selectRole, + (state: IRoleState) => state.selectedId +); + +export const selectRoleSelected = createSelector( + selectRoleEntities, + selectRoleSelectedId, + (entities, selectedId) => { + return ( + selectedId && selectedId in entities ? entities[selectedId] : null + ) as IRole | null; + } +); + +export const selectActionStatus = createSelector( + selectRole, + (state: IRoleState) => state.actionStatus +); + +export const selectError = createSelector( + selectRole, + (state: IRoleState) => state.error +); diff --git a/packages/core/state/src/lib/+state/management/country/country.actions.ts b/packages/core/state/src/lib/+state/management/country/country.actions.ts index dcdae35c..dc90cb66 100644 --- a/packages/core/state/src/lib/+state/management/country/country.actions.ts +++ b/packages/core/state/src/lib/+state/management/country/country.actions.ts @@ -21,6 +21,21 @@ export const countryReadRequestFail = createAction( props<{ error: string }>() ); +export const countryReadOneByIdRequest = createAction( + '[COUNTRY] Read one by id request', + props<{ payload: { id: string } }>() +); + +export const countryReadOneByIdRequestSuccess = createAction( + '[COUNTRY] Read one by id success', + props<{ payload: ICountry }>() +); + +export const countryReadOneByIdRequestFail = createAction( + '[COUNTRY] Read one by id fail', + props<{ error: string }>() +); + export const setSelectedId = createAction( '[COUNTRY] Set selected id', props<{ payload: string | null }>() diff --git a/packages/core/state/src/lib/+state/management/country/country.effects.ts b/packages/core/state/src/lib/+state/management/country/country.effects.ts index b26bd4b5..388f1a88 100644 --- a/packages/core/state/src/lib/+state/management/country/country.effects.ts +++ b/packages/core/state/src/lib/+state/management/country/country.effects.ts @@ -2,9 +2,13 @@ import { Injectable } from '@angular/core'; import { Router } from '@angular/router'; import { Actions, createEffect, ofType } from '@ngrx/effects'; import { of } from 'rxjs'; -import { catchError, map, switchMap, tap } from 'rxjs/operators'; +import { catchError, exhaustMap, map, switchMap, tap } from 'rxjs/operators'; import { ROUTER } from '@console-core/config'; +import { + IoRestorecommerceResourcebaseFilterOperation, + IoRestorecommerceResourcebaseFilterValueType, +} from '@console-core/graphql'; import { ENotificationTypes, ICountry, @@ -21,7 +25,7 @@ export class CountryEffects { countryReadRequest$ = createEffect(() => { return this.actions$.pipe( ofType(countryActions.countryReadRequest), - switchMap(({ payload }) => + exhaustMap(({ payload }) => this.countryService.read(payload).pipe( tap((result) => { this.errorHandlingService.checkStatusAndThrow( @@ -43,6 +47,53 @@ export class CountryEffects { ); }); + countryReadOneByIdRequest$ = createEffect(() => { + return this.actions$.pipe( + ofType(countryActions.countryReadOneByIdRequest), + exhaustMap(({ payload }) => + this.countryService + .read({ + filters: [ + { + filters: [ + { + field: 'id', + value: payload.id, + type: IoRestorecommerceResourcebaseFilterValueType.String, + operation: IoRestorecommerceResourcebaseFilterOperation.Eq, + }, + ], + }, + ], + limit: 1, + }) + .pipe( + tap((result) => { + this.errorHandlingService.checkStatusAndThrow( + result?.data?.master_data?.country?.Read?.details + ?.operationStatus as TOperationStatus + ); + }), + map((result) => { + const payload = + result?.data?.master_data?.country?.Read?.details?.items?.pop() + ?.payload as ICountry; + return countryActions.countryReadOneByIdRequestSuccess({ + payload, + }); + }), + catchError((error: Error) => + of( + countryActions.countryReadOneByIdRequestFail({ + error: error.message, + }) + ) + ) + ) + ) + ); + }); + countryCreateRequest$ = createEffect(() => { return this.actions$.pipe( ofType(countryActions.countryCreateRequest), @@ -175,6 +226,7 @@ export class CountryEffects { return this.actions$.pipe( ofType( countryActions.countryReadRequestFail, + countryActions.countryReadOneByIdRequestFail, countryActions.countryCreateFail, countryActions.countryUpdateFail, countryActions.countryRemoveFail diff --git a/packages/core/state/src/lib/+state/management/country/country.facade.ts b/packages/core/state/src/lib/+state/management/country/country.facade.ts index a8b6959a..cd1dbd68 100644 --- a/packages/core/state/src/lib/+state/management/country/country.facade.ts +++ b/packages/core/state/src/lib/+state/management/country/country.facade.ts @@ -32,6 +32,8 @@ export class CountryFacade { // Actions read = (payload: IIoRestorecommerceResourcebaseReadRequest) => this.store.dispatch(countryActions.countryReadRequest({ payload })); + readOneById = (payload: { id: string }) => + this.store.dispatch(countryActions.countryReadOneByIdRequest({ payload })); setSelectedId = (payload: string | null) => this.store.dispatch(countryActions.setSelectedId({ payload })); create = (payload: IIoRestorecommerceCountryCountryList) => diff --git a/packages/core/state/src/lib/+state/management/country/country.reducer.ts b/packages/core/state/src/lib/+state/management/country/country.reducer.ts index 992c0861..15cf266c 100644 --- a/packages/core/state/src/lib/+state/management/country/country.reducer.ts +++ b/packages/core/state/src/lib/+state/management/country/country.reducer.ts @@ -38,6 +38,32 @@ const reducer = createReducer( error, }) ), + on( + countryActions.countryReadOneByIdRequest, + (state): ICountryState => ({ + ...state, + actionStatus: EActionStatus.Requesting, + }) + ), + on( + countryActions.countryReadOneByIdRequestSuccess, + (state, { payload }): ICountryState => + adapter.updateOne( + { id: payload.id, changes: payload }, + { + ...state, + actionStatus: EActionStatus.Succeeded, + } + ) + ), + on( + countryActions.countryReadOneByIdRequestFail, + (state, { error }): ICountryState => ({ + ...state, + actionStatus: EActionStatus.Failed, + error, + }) + ), on( countryActions.setSelectedId, (state, { payload }): ICountryState => ({ diff --git a/packages/core/state/src/lib/+state/management/iam/iam.actions.ts b/packages/core/state/src/lib/+state/management/iam/iam.actions.ts new file mode 100644 index 00000000..7a476c69 --- /dev/null +++ b/packages/core/state/src/lib/+state/management/iam/iam.actions.ts @@ -0,0 +1,87 @@ +import { createAction, props } from '@ngrx/store'; + +import { + IIoRestorecommerceUserUserList, + IIoRestorecommerceResourcebaseReadRequest, +} from '@console-core/graphql'; +import { IUser } from '@console-core/types'; + +export const userReadRequest = createAction( + '[IAM] Read request', + props<{ payload: IIoRestorecommerceResourcebaseReadRequest }>() +); + +export const userReadRequestSuccess = createAction( + '[IAM] Read success', + props<{ payload: IUser[] }>() +); + +export const userReadRequestFail = createAction( + '[IAM] Read fail', + props<{ error: string }>() +); + +export const userReadOneByIdRequest = createAction( + '[IAM] Read one by id request', + props<{ payload: { id: string } }>() +); + +export const userReadOneByIdRequestSuccess = createAction( + '[IAM] Read one by id success', + props<{ payload: IUser }>() +); + +export const userReadOneByIdRequestFail = createAction( + '[IAM] Read one by id fail', + props<{ error: string }>() +); + +export const setSelectedId = createAction( + '[IAM] Set selected id', + props<{ payload: string | null }>() +); + +export const userCreateRequest = createAction( + '[IAM] User create request', + props<{ payload: IIoRestorecommerceUserUserList }>() +); + +export const userCreateSuccess = createAction( + '[IAM] User create success', + props<{ payload: IUser }>() +); + +export const userCreateFail = createAction( + '[IAM] User create fail', + props<{ error: string }>() +); + +export const userUpdateRequest = createAction( + '[IAM] User update request', + props<{ payload: IIoRestorecommerceUserUserList }>() +); + +export const userUpdateSuccess = createAction( + '[IAM] User update success', + props<{ payload: IUser }>() +); + +export const userUpdateFail = createAction( + '[IAM] User update fail', + props<{ error: string }>() +); + +export const userRemoveRequest = createAction( + '[IAM] User remove request', + props<{ payload: { id: string } }>() +); + +export const userRemoveSuccess = createAction( + '[IAM] User remove success', + props<{ payload: { id: string } }>() +); + +export const userRemoveFail = createAction( + '[IAM] User remove fail', + props<{ error: string }>() +); diff --git a/packages/core/state/src/lib/+state/management/iam/iam.effects.ts b/packages/core/state/src/lib/+state/management/iam/iam.effects.ts new file mode 100644 index 00000000..cfa19330 --- /dev/null +++ b/packages/core/state/src/lib/+state/management/iam/iam.effects.ts @@ -0,0 +1,261 @@ +import { Injectable } from '@angular/core'; +import { Router } from '@angular/router'; +import { Actions, createEffect, ofType } from '@ngrx/effects'; +import { of } from 'rxjs'; +import { catchError, exhaustMap, map, switchMap, tap } from 'rxjs/operators'; + +import { ROUTER } from '@console-core/config'; +import { + IoRestorecommerceResourcebaseFilterOperation, + IoRestorecommerceResourcebaseFilterValueType, +} from '@console-core/graphql'; +import { + ENotificationTypes, + IUser, + TOperationStatus, +} from '@console-core/types'; + +import { UserService, ErrorHandlingService } from '../../../services'; +import { AppFacade } from '../../app'; + +import * as userActions from './iam.actions'; + +@Injectable() +export class IamEffects { + userReadRequest$ = createEffect(() => { + return this.actions$.pipe( + ofType(userActions.userReadRequest), + exhaustMap(() => + this.userService.read({}).pipe( + tap((result) => { + this.errorHandlingService.checkStatusAndThrow( + result?.data?.identity?.user?.Read?.details + ?.operationStatus as TOperationStatus + ); + }), + map((result) => { + const payload = ( + result?.data?.identity?.user?.Read?.details?.items || [] + )?.map((item) => + this.userService.getUserWithRolesAndFullName( + item?.payload as IUser + ) + ) as IUser[]; + return userActions.userReadRequestSuccess({ payload }); + }), + catchError((error: Error) => + of(userActions.userReadRequestFail({ error: error.message })) + ) + ) + ) + ); + }); + + userReadOneByIdRequest$ = createEffect(() => { + return this.actions$.pipe( + ofType(userActions.userReadOneByIdRequest), + exhaustMap(({ payload }) => + this.userService + .read({ + filters: [ + { + filters: [ + { + field: 'id', + value: payload.id, + type: IoRestorecommerceResourcebaseFilterValueType.String, + operation: IoRestorecommerceResourcebaseFilterOperation.Eq, + }, + ], + }, + ], + limit: 1, + }) + .pipe( + tap((result) => { + this.errorHandlingService.checkStatusAndThrow( + result?.data?.identity?.user?.Read?.details + ?.operationStatus as TOperationStatus + ); + }), + map((result) => { + const first = + result?.data?.identity?.user?.Read?.details?.items?.pop() + ?.payload as IUser; + const payload = + this.userService.getUserWithRolesAndFullName(first); + return userActions.userReadOneByIdRequestSuccess({ payload }); + }), + catchError((error: Error) => + of( + userActions.userReadOneByIdRequestFail({ + error: error.message, + }) + ) + ) + ) + ) + ); + }); + + userCreateRequest$ = createEffect(() => { + return this.actions$.pipe( + ofType(userActions.userCreateRequest), + switchMap(({ payload }) => + this.userService.mutate(payload).pipe( + tap((result) => { + this.errorHandlingService.checkStatusAndThrow( + result?.data?.identity?.user?.Mutate?.details + ?.operationStatus as TOperationStatus + ); + }), + map((result) => { + const payload = + result?.data?.identity?.user?.Mutate?.details?.items?.pop() + ?.payload as IUser; + + return userActions.userCreateSuccess({ + payload: this.userService.getUserWithRolesAndFullName(payload), + }); + }), + catchError((error: Error) => + of(userActions.userCreateFail({ error: error.message })) + ) + ) + ) + ); + }); + + userCreateSuccess$ = createEffect( + () => { + return this.actions$.pipe( + ofType(userActions.userCreateSuccess), + tap(() => { + this.appFacade.addNotification({ + content: 'user created', + type: ENotificationTypes.Success, + }); + }), + tap(({ payload }) => { + this.router.navigate( + ROUTER.pages.main.children.management.children.iam.children.edit.getLink( + { id: payload.id } + ) + ); + }) + ); + }, + { dispatch: false } + ); + + userUpdateRequest$ = createEffect(() => { + return this.actions$.pipe( + ofType(userActions.userUpdateRequest), + switchMap(({ payload }) => + this.userService.mutate(payload).pipe( + tap((result) => { + this.errorHandlingService.checkStatusAndThrow( + result?.data?.identity?.user?.Mutate?.details + ?.operationStatus as TOperationStatus + ); + }), + map((result) => { + const payload = + result?.data?.identity?.user?.Mutate?.details?.items?.pop() + ?.payload as IUser; + return userActions.userUpdateSuccess({ + payload: this.userService.getUserWithRolesAndFullName(payload), + }); + }), + catchError((error: Error) => + of(userActions.userUpdateFail({ error: error.message })) + ) + ) + ) + ); + }); + + userUpdateSuccess$ = createEffect( + () => { + return this.actions$.pipe( + ofType(userActions.userUpdateSuccess), + tap(() => { + this.appFacade.addNotification({ + content: 'user updated', + type: ENotificationTypes.Success, + }); + }) + ); + }, + { dispatch: false } + ); + + userRemoveRequest$ = createEffect(() => { + return this.actions$.pipe( + ofType(userActions.userRemoveRequest), + switchMap(({ payload }) => { + const id = payload.id; + return this.userService.remove({ ids: [id] }).pipe( + tap((result) => { + this.errorHandlingService.checkStatusAndThrow( + result?.data?.identity?.user?.Delete?.details + ?.operationStatus as TOperationStatus + ); + }), + map(() => { + return userActions.userRemoveSuccess({ + payload: { id }, + }); + }), + catchError((error: Error) => + of(userActions.userRemoveFail({ error: error.message })) + ) + ); + }) + ); + }); + + userRemoveSuccess$ = createEffect( + () => { + return this.actions$.pipe( + ofType(userActions.userRemoveSuccess), + tap(() => { + this.appFacade.addNotification({ + content: 'user deleted', + type: ENotificationTypes.Success, + }); + }) + ); + }, + { dispatch: false } + ); + + handleNotificationErrors$ = createEffect( + () => { + return this.actions$.pipe( + ofType( + userActions.userReadRequestFail, + userActions.userReadOneByIdRequestFail, + userActions.userCreateFail, + userActions.userUpdateFail, + userActions.userRemoveFail + ), + tap(({ error }) => { + this.appFacade.addNotification({ + content: error ?? 'unknown error', + type: ENotificationTypes.Error, + }); + }) + ); + }, + { dispatch: false } + ); + + constructor( + private readonly router: Router, + private readonly actions$: Actions, + private readonly appFacade: AppFacade, + private readonly userService: UserService, + private readonly errorHandlingService: ErrorHandlingService + ) {} +} diff --git a/packages/core/state/src/lib/+state/management/iam/iam.facade.ts b/packages/core/state/src/lib/+state/management/iam/iam.facade.ts new file mode 100644 index 00000000..e4357e2e --- /dev/null +++ b/packages/core/state/src/lib/+state/management/iam/iam.facade.ts @@ -0,0 +1,39 @@ +import { Injectable } from '@angular/core'; +import { Store } from '@ngrx/store'; + +import { + IIoRestorecommerceUserUserList, + IIoRestorecommerceResourcebaseReadRequest, +} from '@console-core/graphql'; + +import * as userActions from './iam.actions'; +import * as userSelectors from './iam.selectors'; + +@Injectable() +export class IamFacade { + // Selectors + readonly ids$ = this.store.select(userSelectors.selectUserIds); + readonly entities$ = this.store.select(userSelectors.selectUserEntities); + readonly all$ = this.store.select(userSelectors.selectUserAll); + readonly total$ = this.store.select(userSelectors.selectUserTotal); + readonly selectedId$ = this.store.select(userSelectors.selectUserSelectedId); + readonly selected$ = this.store.select(userSelectors.selectUserSelected); + readonly actionStatus$ = this.store.select(userSelectors.selectActionStatus); + readonly error$ = this.store.select(userSelectors.selectError); + + // Actions + read = (payload: IIoRestorecommerceResourcebaseReadRequest) => + this.store.dispatch(userActions.userReadRequest({ payload })); + readOneById = (payload: { id: string }) => + this.store.dispatch(userActions.userReadOneByIdRequest({ payload })); + setSelectedId = (payload: string | null) => + this.store.dispatch(userActions.setSelectedId({ payload })); + create = (payload: IIoRestorecommerceUserUserList) => + this.store.dispatch(userActions.userCreateRequest({ payload })); + update = (payload: IIoRestorecommerceUserUserList) => + this.store.dispatch(userActions.userUpdateRequest({ payload })); + remove = (payload: { id: string }) => + this.store.dispatch(userActions.userRemoveRequest({ payload })); + + constructor(private readonly store: Store) {} +} diff --git a/packages/core/state/src/lib/+state/management/iam/iam.reducer.ts b/packages/core/state/src/lib/+state/management/iam/iam.reducer.ts new file mode 100644 index 00000000..003fc505 --- /dev/null +++ b/packages/core/state/src/lib/+state/management/iam/iam.reducer.ts @@ -0,0 +1,149 @@ +import { EntityAdapter, createEntityAdapter } from '@ngrx/entity'; +import { Action, createReducer, on } from '@ngrx/store'; + +import { EActionStatus, IUser, IIamState } from '@console-core/types'; + +import * as userActions from './iam.actions'; + +export const adapter: EntityAdapter = createEntityAdapter(); + +export const initialState: IIamState = adapter.getInitialState({ + selectedId: null, + actionStatus: EActionStatus.INIT, + error: null, +}); + +const reducer = createReducer( + initialState, + on( + userActions.userReadRequest, + (state): IIamState => ({ + ...state, + actionStatus: EActionStatus.Requesting, + }) + ), + on( + userActions.userReadRequestSuccess, + (state, { payload }): IIamState => + adapter.setAll(payload, { + ...state, + actionStatus: EActionStatus.Succeeded, + }) + ), + on( + userActions.userReadRequestFail, + (state, { error }): IIamState => ({ + ...state, + actionStatus: EActionStatus.Failed, + error, + }) + ), + on( + userActions.userReadOneByIdRequest, + (state): IIamState => ({ + ...state, + actionStatus: EActionStatus.Requesting, + }) + ), + on( + userActions.userReadOneByIdRequestSuccess, + (state, { payload }): IIamState => + adapter.updateOne( + { id: payload.id, changes: payload }, + { + ...state, + actionStatus: EActionStatus.Succeeded, + } + ) + ), + on( + userActions.userReadOneByIdRequestFail, + (state, { error }): IIamState => ({ + ...state, + actionStatus: EActionStatus.Failed, + error, + }) + ), + on( + userActions.setSelectedId, + (state, { payload }): IIamState => ({ + ...state, + selectedId: payload, + }) + ), + on( + userActions.userCreateRequest, + (state): IIamState => ({ + ...state, + actionStatus: EActionStatus.Mutating, + }) + ), + on( + userActions.userCreateSuccess, + (state, { payload }): IIamState => + adapter.addOne(payload, { + ...state, + actionStatus: EActionStatus.Succeeded, + }) + ), + on( + userActions.userCreateFail, + (state, { error }): IIamState => ({ + ...state, + actionStatus: EActionStatus.Failed, + error, + }) + ), + on( + userActions.userUpdateRequest, + (state): IIamState => ({ + ...state, + actionStatus: EActionStatus.Mutating, + }) + ), + on( + userActions.userUpdateSuccess, + (state, { payload }): IIamState => + adapter.updateOne( + { id: payload.id, changes: payload }, + { + ...state, + actionStatus: EActionStatus.Succeeded, + } + ) + ), + on( + userActions.userUpdateFail, + (state, { error }): IIamState => ({ + ...state, + actionStatus: EActionStatus.Failed, + error, + }) + ), + on( + userActions.userRemoveRequest, + (state): IIamState => ({ + ...state, + actionStatus: EActionStatus.Mutating, + }) + ), + on( + userActions.userRemoveSuccess, + (state, { payload }): IIamState => + adapter.removeOne(payload.id, { + ...state, + actionStatus: EActionStatus.Succeeded, + }) + ), + on( + userActions.userRemoveFail, + (state, { error }): IIamState => ({ + ...state, + actionStatus: EActionStatus.Failed, + error, + }) + ) +); + +export const iamReducer = (state: IIamState | undefined, action: Action) => + reducer(state, action); diff --git a/packages/core/state/src/lib/+state/management/iam/iam.selectors.ts b/packages/core/state/src/lib/+state/management/iam/iam.selectors.ts new file mode 100644 index 00000000..c38d0e9c --- /dev/null +++ b/packages/core/state/src/lib/+state/management/iam/iam.selectors.ts @@ -0,0 +1,46 @@ +import { createFeatureSelector, createSelector } from '@ngrx/store'; + +import { STORE } from '@console-core/config'; +import { IUser, IIamState } from '@console-core/types'; + +import { adapter } from './iam.reducer'; + +export const selectUser = createFeatureSelector( + STORE.states.iamState +); + +const { selectIds, selectEntities, selectAll, selectTotal } = + adapter.getSelectors(); + +export const selectUserIds = createSelector(selectUser, selectIds); + +export const selectUserEntities = createSelector(selectUser, selectEntities); + +export const selectUserAll = createSelector(selectUser, selectAll); + +export const selectUserTotal = createSelector(selectUser, selectTotal); + +export const selectUserSelectedId = createSelector( + selectUser, + (state: IIamState) => state.selectedId +); + +export const selectUserSelected = createSelector( + selectUserEntities, + selectUserSelectedId, + (entities, selectedId) => { + return ( + selectedId && selectedId in entities ? entities[selectedId] : null + ) as IUser | null; + } +); + +export const selectActionStatus = createSelector( + selectUser, + (state: IIamState) => state.actionStatus +); + +export const selectError = createSelector( + selectUser, + (state: IIamState) => state.error +); diff --git a/packages/core/state/src/lib/+state/management/iam/index.ts b/packages/core/state/src/lib/+state/management/iam/index.ts new file mode 100644 index 00000000..ec5e81fb --- /dev/null +++ b/packages/core/state/src/lib/+state/management/iam/index.ts @@ -0,0 +1,3 @@ +export { IamEffects } from './iam.effects'; +export { IamFacade } from './iam.facade'; +export { iamReducer } from './iam.reducer'; diff --git a/packages/core/state/src/lib/+state/management/index.ts b/packages/core/state/src/lib/+state/management/index.ts index 5f2f31b4..00deee4a 100644 --- a/packages/core/state/src/lib/+state/management/index.ts +++ b/packages/core/state/src/lib/+state/management/index.ts @@ -1,3 +1,5 @@ +export * from './access-control'; export * from './country'; +export * from './iam'; export * from './locale'; export * from './timezone'; diff --git a/packages/core/state/src/lib/+state/order/order.actions.ts b/packages/core/state/src/lib/+state/order/order.actions.ts index 1d6f9094..03c2f35c 100644 --- a/packages/core/state/src/lib/+state/order/order.actions.ts +++ b/packages/core/state/src/lib/+state/order/order.actions.ts @@ -21,6 +21,21 @@ export const orderReadRequestFail = createAction( props<{ error: string }>() ); +export const orderReadOneByIdRequest = createAction( + '[ORDER] Read one by id request', + props<{ payload: { id: string } }>() +); + +export const orderReadOneByIdRequestSuccess = createAction( + '[ORDER] Read one by id success', + props<{ payload: IOrder }>() +); + +export const orderReadOneByIdRequestFail = createAction( + '[ORDER] Read one by id fail', + props<{ error: string }>() +); + export const setSelectedId = createAction( '[ORDER] Set selected id', props<{ payload: string | null }>() diff --git a/packages/core/state/src/lib/+state/order/order.effects.ts b/packages/core/state/src/lib/+state/order/order.effects.ts index c9d0e535..a2b480cb 100644 --- a/packages/core/state/src/lib/+state/order/order.effects.ts +++ b/packages/core/state/src/lib/+state/order/order.effects.ts @@ -2,9 +2,13 @@ import { Injectable } from '@angular/core'; import { Router } from '@angular/router'; import { Actions, createEffect, ofType } from '@ngrx/effects'; import { of } from 'rxjs'; -import { catchError, map, switchMap, tap } from 'rxjs/operators'; +import { catchError, exhaustMap, map, switchMap, tap } from 'rxjs/operators'; import { ROUTER } from '@console-core/config'; +import { + IoRestorecommerceResourcebaseFilterOperation, + IoRestorecommerceResourcebaseFilterValueType, +} from '@console-core/graphql'; import { ENotificationTypes, IOrder, @@ -21,7 +25,7 @@ export class OrderEffects { orderReadRequest$ = createEffect(() => { return this.actions$.pipe( ofType(orderActions.orderReadRequest), - switchMap(({ payload }) => + exhaustMap(({ payload }) => this.orderService.read(payload).pipe( tap((result) => { this.errorHandlingService.checkStatusAndThrow( @@ -43,6 +47,51 @@ export class OrderEffects { ); }); + orderReadOneByIdRequest$ = createEffect(() => { + return this.actions$.pipe( + ofType(orderActions.orderReadOneByIdRequest), + exhaustMap(({ payload }) => + this.orderService + .read({ + filters: [ + { + filters: [ + { + field: 'id', + value: payload.id, + type: IoRestorecommerceResourcebaseFilterValueType.String, + operation: IoRestorecommerceResourcebaseFilterOperation.Eq, + }, + ], + }, + ], + limit: 1, + }) + .pipe( + tap((result) => { + this.errorHandlingService.checkStatusAndThrow( + result?.data?.ordering?.order?.Read?.details + ?.operationStatus as TOperationStatus + ); + }), + map((result) => { + const payload = + result?.data?.ordering?.order?.Read?.details?.items?.pop() + ?.payload as IOrder; + return orderActions.orderReadOneByIdRequestSuccess({ payload }); + }), + catchError((error: Error) => + of( + orderActions.orderReadOneByIdRequestFail({ + error: error.message, + }) + ) + ) + ) + ) + ); + }); + orderCreateRequest$ = createEffect(() => { return this.actions$.pipe( ofType(orderActions.orderCreateRequest), @@ -175,6 +224,7 @@ export class OrderEffects { return this.actions$.pipe( ofType( orderActions.orderReadRequestFail, + orderActions.orderReadOneByIdRequestFail, orderActions.orderCreateFail, orderActions.orderUpdateFail, orderActions.orderRemoveFail diff --git a/packages/core/state/src/lib/+state/order/order.facade.ts b/packages/core/state/src/lib/+state/order/order.facade.ts index 5c485bb9..17f13b3e 100644 --- a/packages/core/state/src/lib/+state/order/order.facade.ts +++ b/packages/core/state/src/lib/+state/order/order.facade.ts @@ -26,6 +26,8 @@ export class OrderFacade { // Actions read = (payload: IIoRestorecommerceResourcebaseReadRequest) => this.store.dispatch(orderActions.orderReadRequest({ payload })); + readOneById = (payload: { id: string }) => + this.store.dispatch(orderActions.orderReadOneByIdRequest({ payload })); setSelectedId = (payload: string | null) => this.store.dispatch(orderActions.setSelectedId({ payload })); create = (payload: IIoRestorecommerceOrderOrderList) => diff --git a/packages/core/state/src/lib/+state/order/order.reducer.ts b/packages/core/state/src/lib/+state/order/order.reducer.ts index 686e9b3c..2f64c2a2 100644 --- a/packages/core/state/src/lib/+state/order/order.reducer.ts +++ b/packages/core/state/src/lib/+state/order/order.reducer.ts @@ -38,6 +38,32 @@ const reducer = createReducer( error, }) ), + on( + orderActions.orderReadOneByIdRequest, + (state): IOrderState => ({ + ...state, + actionStatus: EActionStatus.Requesting, + }) + ), + on( + orderActions.orderReadOneByIdRequestSuccess, + (state, { payload }): IOrderState => + adapter.updateOne( + { id: payload.id, changes: payload }, + { + ...state, + actionStatus: EActionStatus.Succeeded, + } + ) + ), + on( + orderActions.orderReadOneByIdRequestFail, + (state, { error }): IOrderState => ({ + ...state, + actionStatus: EActionStatus.Failed, + error, + }) + ), on( orderActions.setSelectedId, (state, { payload }): IOrderState => ({ diff --git a/packages/core/state/src/lib/+state/product/product.actions.ts b/packages/core/state/src/lib/+state/product/product.actions.ts index 14b3bdd4..2a98a92d 100644 --- a/packages/core/state/src/lib/+state/product/product.actions.ts +++ b/packages/core/state/src/lib/+state/product/product.actions.ts @@ -26,6 +26,21 @@ export const productReadRequestFail = createAction( props<{ error: string }>() ); +export const productReadOneByIdRequest = createAction( + '[PRODUCT] Read one by id request', + props<{ payload: { id: string } }>() +); + +export const productReadOneByIdRequestSuccess = createAction( + '[PRODUCT] Read one by id success', + props<{ payload: IProduct }>() +); + +export const productReadOneByIdRequestFail = createAction( + '[PRODUCT] Read one by id fail', + props<{ error: string }>() +); + export const setSelectedId = createAction( '[PRODUCT] Set selected id', props<{ payload: string | null }>() diff --git a/packages/core/state/src/lib/+state/product/product.effects.ts b/packages/core/state/src/lib/+state/product/product.effects.ts index 35829c6d..613e5d26 100644 --- a/packages/core/state/src/lib/+state/product/product.effects.ts +++ b/packages/core/state/src/lib/+state/product/product.effects.ts @@ -2,9 +2,13 @@ import { Injectable } from '@angular/core'; import { Router } from '@angular/router'; import { Actions, createEffect, ofType } from '@ngrx/effects'; import { of } from 'rxjs'; -import { catchError, map, switchMap, tap } from 'rxjs/operators'; +import { catchError, exhaustMap, map, switchMap, tap } from 'rxjs/operators'; import { ROUTER } from '@console-core/config'; +import { + IoRestorecommerceResourcebaseFilterOperation, + IoRestorecommerceResourcebaseFilterValueType, +} from '@console-core/graphql'; import { ENotificationTypes, IProduct, @@ -15,6 +19,7 @@ import { ErrorHandlingService, ProductService } from '../../services'; import { AppFacade } from '../app'; import * as productActions from './product.actions'; +import { productReadOneByIdRequest } from './product.actions'; @Injectable() export class ProductEffects { @@ -22,7 +27,7 @@ export class ProductEffects { let isLoadMore = false; return this.actions$.pipe( ofType(productActions.productReadRequest), - switchMap(({ payload }) => + exhaustMap(({ payload }) => this.productService.read(payload).pipe( tap((result) => { if (payload.offset) { @@ -53,6 +58,47 @@ export class ProductEffects { ); }); + productReadOneByIdRequest$ = createEffect(() => { + return this.actions$.pipe( + ofType(productReadOneByIdRequest), + exhaustMap(({ payload }) => + this.productService + .read({ + filters: [ + { + filters: [ + { + field: 'id', + value: payload.id, + type: IoRestorecommerceResourcebaseFilterValueType.String, + operation: IoRestorecommerceResourcebaseFilterOperation.Eq, + }, + ], + }, + ], + limit: 1, + }) + .pipe( + map((result) => { + const payload = + result?.data?.catalog?.product?.Read?.details?.items?.pop() + ?.payload as IProduct; + return productActions.productReadOneByIdRequestSuccess({ + payload, + }); + }), + catchError((error: Error) => + of( + productActions.productReadOneByIdRequestFail({ + error: error.message, + }) + ) + ) + ) + ) + ); + }); + productCreateRequest$ = createEffect(() => { return this.actions$.pipe( ofType(productActions.productCreateRequest), @@ -185,6 +231,7 @@ export class ProductEffects { return this.actions$.pipe( ofType( productActions.productReadRequestFail, + productActions.productReadOneByIdRequestFail, productActions.productCreateFail, productActions.productUpdateFail, productActions.productRemoveFail diff --git a/packages/core/state/src/lib/+state/product/product.facade.ts b/packages/core/state/src/lib/+state/product/product.facade.ts index b7775288..908ee021 100644 --- a/packages/core/state/src/lib/+state/product/product.facade.ts +++ b/packages/core/state/src/lib/+state/product/product.facade.ts @@ -32,6 +32,8 @@ export class ProductFacade { // Actions read = (payload: IIoRestorecommerceResourcebaseReadRequest) => this.store.dispatch(productActions.productReadRequest({ payload })); + readOneById = (payload: { id: string }) => + this.store.dispatch(productActions.productReadOneByIdRequest({ payload })); setSelectedId = (payload: string | null) => this.store.dispatch(productActions.setSelectedId({ payload })); create = (payload: IIoRestorecommerceProductProductList) => diff --git a/packages/core/state/src/lib/+state/product/product.reducer.ts b/packages/core/state/src/lib/+state/product/product.reducer.ts index bcfcf156..d7e30c46 100644 --- a/packages/core/state/src/lib/+state/product/product.reducer.ts +++ b/packages/core/state/src/lib/+state/product/product.reducer.ts @@ -42,6 +42,29 @@ const reducer = createReducer( error, }) ), + on( + productActions.productReadOneByIdRequest, + (state): IProductState => ({ + ...state, + actionStatus: EActionStatus.Requesting, + }) + ), + on( + productActions.productReadOneByIdRequestSuccess, + (state, { payload }): IProductState => + adapter.addOne(payload, { + ...state, + actionStatus: EActionStatus.Succeeded, + }) + ), + on( + productActions.productReadOneByIdRequestFail, + (state, { error }): IProductState => ({ + ...state, + actionStatus: EActionStatus.Failed, + error, + }) + ), on( productActions.setSelectedId, (state, { payload }): IProductState => ({ diff --git a/packages/core/state/src/lib/core-state.module.ts b/packages/core/state/src/lib/core-state.module.ts index 3045d206..d023e037 100644 --- a/packages/core/state/src/lib/core-state.module.ts +++ b/packages/core/state/src/lib/core-state.module.ts @@ -21,6 +21,9 @@ import { FulfillmentEffects, FulfillmentFacade, fulfillmentReducer, + IamEffects, + IamFacade, + iamReducer, InvoiceEffects, InvoiceFacade, invoiceReducer, @@ -33,6 +36,9 @@ import { ProductEffects, ProductFacade, productReducer, + RoleEffects, + RoleFacade, + roleReducer, RouterFacade, TimezoneEffects, TimezoneFacade, @@ -46,6 +52,8 @@ const facades = [ CountryFacade, FulfillmentFacade, InvoiceFacade, + IamFacade, + RoleFacade, LocaleFacade, OrderFacade, ProductFacade, @@ -67,6 +75,10 @@ const facades = [ EffectsModule.forFeature([FulfillmentEffects]), StoreModule.forFeature(STORE.states.invoiceState, invoiceReducer), EffectsModule.forFeature([InvoiceEffects]), + StoreModule.forFeature(STORE.states.iamState, iamReducer), + EffectsModule.forFeature([IamEffects]), + StoreModule.forFeature(STORE.states.roleState, roleReducer), + EffectsModule.forFeature([RoleEffects]), StoreModule.forFeature(STORE.states.localeState, localeReducer), EffectsModule.forFeature([LocaleEffects]), StoreModule.forFeature(STORE.states.orderState, orderReducer), diff --git a/packages/core/state/src/lib/services/error-handling.service.ts b/packages/core/state/src/lib/services/error-handling.service.ts index d080e62e..290fb873 100644 --- a/packages/core/state/src/lib/services/error-handling.service.ts +++ b/packages/core/state/src/lib/services/error-handling.service.ts @@ -2,10 +2,13 @@ import { Injectable } from '@angular/core'; import { TOperationStatus } from '@console-core/types'; +import { AuthnFacade } from '../+state'; + @Injectable({ providedIn: 'root', }) export class ErrorHandlingService { + constructor(private readonly authFacade: AuthnFacade) {} public checkStatusAndThrow(status?: TOperationStatus): void { const errorMessages: { [key: number]: string } = { 400: 'bad request', @@ -19,6 +22,10 @@ export class ErrorHandlingService { let errorMessage = ''; if (status?.code) { + if (status.code === 401) { + this.authFacade.signOut(false); + } + if (status?.code in errorMessages) { errorMessage = errorMessages[status?.code]; } else if (status.code !== 200) { diff --git a/packages/core/state/src/lib/services/identity/index.ts b/packages/core/state/src/lib/services/identity/index.ts index 5a7fb96d..ee6b0c7d 100644 --- a/packages/core/state/src/lib/services/identity/index.ts +++ b/packages/core/state/src/lib/services/identity/index.ts @@ -1,2 +1,3 @@ export * from './user.service'; +export * from './role.service'; export * from './authn.service'; diff --git a/packages/core/state/src/lib/services/identity/role.service.ts b/packages/core/state/src/lib/services/identity/role.service.ts new file mode 100644 index 00000000..1d237a1c --- /dev/null +++ b/packages/core/state/src/lib/services/identity/role.service.ts @@ -0,0 +1,45 @@ +import { Injectable } from '@angular/core'; +import { ApolloQueryResult } from '@apollo/client'; +import { MutationResult } from 'apollo-angular'; +import { Observable } from 'rxjs'; + +import { + IIoRestorecommerceResourcebaseDeleteRequest, + IIoRestorecommerceResourcebaseReadRequest, + IIoRestorecommerceRoleRoleList, + IdentityRoleDeleteGQL, + IdentityRoleDeleteMutation, + IdentityRoleMutateGQL, + IdentityRoleMutateMutation, + IdentityRoleReadGQL, + IdentityRoleReadQuery, +} from '@console-core/graphql'; + +@Injectable({ + providedIn: 'root', +}) +export class RoleService { + constructor( + private readonly identityRoleReadGQL: IdentityRoleReadGQL, + private readonly identityRoleMutateGQL: IdentityRoleMutateGQL, + private readonly identityRoleDeleteGQL: IdentityRoleDeleteGQL + ) {} + + read( + payload: IIoRestorecommerceResourcebaseReadRequest + ): Observable> { + return this.identityRoleReadGQL.fetch({ input: payload }); + } + + mutate( + payload: IIoRestorecommerceRoleRoleList + ): Observable> { + return this.identityRoleMutateGQL.mutate({ input: payload }); + } + + remove( + payload: IIoRestorecommerceResourcebaseDeleteRequest + ): Observable> { + return this.identityRoleDeleteGQL.mutate({ input: payload }); + } +} diff --git a/packages/core/state/src/lib/services/identity/user.service.ts b/packages/core/state/src/lib/services/identity/user.service.ts index d1f9ac0b..5e60f93f 100644 --- a/packages/core/state/src/lib/services/identity/user.service.ts +++ b/packages/core/state/src/lib/services/identity/user.service.ts @@ -7,11 +7,7 @@ import { IdentityUserFindByTokenGQL, IdentityUserFindByTokenQuery, IIoRestorecommerceUserFindByTokenRequest, - IdentityUserMutateGQL, - IdentityUserMutateMutation, IIoRestorecommerceUserUserList, - IdentityUserDeleteGQL, - IdentityUserDeleteMutation, IIoRestorecommerceResourcebaseDeleteRequest, IdentityUserFindGQL, IdentityUserFindQuery, @@ -34,22 +30,33 @@ import { IdentityUserRequestPasswordChangeMutation, IIoRestorecommerceUserConfirmPasswordChangeRequest, IdentityUserConfirmPasswordChangeMutation, + IIoRestorecommerceResourcebaseReadRequest, + IdentityUserReadGQL, + IdentityUserReadQuery, + IdentityUserMutateGQL, + IdentityUserMutateMutation, + IdentityUserDeleteGQL, + IdentityUserDeleteMutation, } from '@console-core/graphql'; +import { EUserRoleAssociation, IUser } from '@console-core/types'; @Injectable({ providedIn: 'root', }) export class UserService { + private roleAssociationsCache: { [key: string]: string } = {}; + constructor( private readonly identityUserActivateGQL: IdentityUserActivateGQL, private readonly identityUserFindGQL: IdentityUserFindGQL, private readonly identityUserFindByTokenGQL: IdentityUserFindByTokenGQL, - private readonly identityUserMutateGQL: IdentityUserMutateGQL, private readonly identityUserRequestEmailChangeGQL: IdentityUserRequestEmailChangeGQL, private readonly identityUserConfirmEmailChangeGQL: IdentityUserConfirmEmailChangeGQL, private readonly identityUserRequestPasswordChangeGQL: IdentityUserRequestPasswordChangeGQL, private readonly identityUserConfirmPasswordChangeGQL: IdentityUserConfirmPasswordChangeGQL, private readonly identityUserChangePasswordGQL: IdentityUserChangePasswordGQL, + private readonly identityUserReadGQL: IdentityUserReadGQL, + private readonly identityUserMutateGQL: IdentityUserMutateGQL, private readonly identityUserDeleteGQL: IdentityUserDeleteGQL ) {} @@ -77,12 +84,6 @@ export class UserService { }); } - mutate( - payload: IIoRestorecommerceUserUserList - ): Observable> { - return this.identityUserMutateGQL.mutate({ input: payload }); - } - requestEmailChange( payload: IIoRestorecommerceUserChangeEmailRequest ): Observable> { @@ -117,9 +118,60 @@ export class UserService { return this.identityUserChangePasswordGQL.mutate({ input: payload }); } + read( + payload: IIoRestorecommerceResourcebaseReadRequest + ): Observable> { + return this.identityUserReadGQL.fetch({ input: payload }); + } + + mutate( + payload: IIoRestorecommerceUserUserList + ): Observable> { + return this.identityUserMutateGQL.mutate({ input: payload }); + } + remove( payload: IIoRestorecommerceResourcebaseDeleteRequest ): Observable> { return this.identityUserDeleteGQL.mutate({ input: payload }); } + + getRoleAssociations(user: IUser): string { + const cacheKey = user.id + user.meta.modified; + + if (this.roleAssociationsCache[cacheKey]) { + return this.roleAssociationsCache[cacheKey]; + } + + const result = user.roleAssociations.length + ? [...new Set(user.roleAssociations.map((ra) => ra.role))] + .map( + (ra) => + EUserRoleAssociation[ra as keyof typeof EUserRoleAssociation] + ) + .sort((a, b) => a.localeCompare(b)) + .join(', ') + : ''; + + this.roleAssociationsCache[cacheKey] = result; + + return result; + } + + getUserWithRolesAndFullName(user: IUser): IUser { + return { + ...user, + fullName: `${user.firstName} ${user.lastName}`, + isSuperAdministrator: this.hasUserRoleAssociations( + user, + 'superadministrator-r-id' + ), + isAdministrator: this.hasUserRoleAssociations(user, 'administrator-r-id'), + isUser: this.hasUserRoleAssociations(user, 'user-r-id'), + }; + } + + private hasUserRoleAssociations(user: IUser, roleId: string): boolean { + return !!user.roleAssociations.some((ra) => ra.role === roleId); + } } diff --git a/packages/core/state/src/lib/utils/index.ts b/packages/core/state/src/lib/utils/index.ts index ba6aafde..6c716f7b 100644 --- a/packages/core/state/src/lib/utils/index.ts +++ b/packages/core/state/src/lib/utils/index.ts @@ -1,4 +1,3 @@ export * from './capitalize-first-letter'; export * from './rxjs-filters'; export * from './state'; -export * from './user'; diff --git a/packages/core/state/src/lib/utils/user.ts b/packages/core/state/src/lib/utils/user.ts deleted file mode 100644 index e29c77d9..00000000 --- a/packages/core/state/src/lib/utils/user.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { IUser } from '@console-core/types'; - -const getUserFullName = (user: IUser): string => - `${user?.firstName} ${user?.lastName}`.trim(); - -const hasUserRole = (user: IUser, role: string): boolean => - !!user?.roleAssociations?.some((r) => r.role === role); - -export const getUser = (user: IUser): IUser => ({ - ...user, - fullName: getUserFullName(user), - isSuperAdministrator: hasUserRole(user, 'superadministrator-r-id'), - isAdministrator: hasUserRole(user, 'administrator-r-id'), - isUser: hasUserRole(user, 'user-r-id'), -}); diff --git a/packages/core/types/src/lib/enums/index.ts b/packages/core/types/src/lib/enums/index.ts index ef14196a..c6dacfb5 100644 --- a/packages/core/types/src/lib/enums/index.ts +++ b/packages/core/types/src/lib/enums/index.ts @@ -1,3 +1,4 @@ export * from './action-status'; export * from './notifications'; export * from './router'; +export * from './user'; diff --git a/packages/core/types/src/lib/enums/user.ts b/packages/core/types/src/lib/enums/user.ts new file mode 100644 index 00000000..a411c36d --- /dev/null +++ b/packages/core/types/src/lib/enums/user.ts @@ -0,0 +1,12 @@ +// Export an enum for user roles +export enum EUserRoleAssociation { + 'superadministrator-r-id' = 'Superadministrator', + 'administrator-r-id' = 'Administrator', + 'sales-r-id' = 'Sales', + 'moderator-r-id' = 'Moderator', + 'scoped-r-id' = 'Scoped', + 'customer-r-id' = 'Customer', + 'member-r-id' = 'Member', + 'user-r-id' = 'User', + 'unauthenticated-r-id' = 'Unauthenticated', +} diff --git a/packages/core/types/src/lib/interfaces/entities/country.ts b/packages/core/types/src/lib/interfaces/entities/country.ts index c5ac6077..d5eceaef 100644 --- a/packages/core/types/src/lib/interfaces/entities/country.ts +++ b/packages/core/types/src/lib/interfaces/entities/country.ts @@ -11,6 +11,7 @@ export interface ICountry | 'geographicalName' | 'economicAreas' | 'meta' + | '__typename' > { id: string; name: string; diff --git a/packages/core/types/src/lib/interfaces/entities/fulfillment.ts b/packages/core/types/src/lib/interfaces/entities/fulfillment.ts index 9a08b4e3..a4a8f44b 100644 --- a/packages/core/types/src/lib/interfaces/entities/fulfillment.ts +++ b/packages/core/types/src/lib/interfaces/entities/fulfillment.ts @@ -5,7 +5,7 @@ import { IMeta } from './meta'; export interface IFulfillment extends Omit< IoRestorecommerceFulfillmentFulfillment, - 'id' | 'customerId' | 'shopId' | 'userId' | 'meta' + 'id' | 'customerId' | 'shopId' | 'userId' | 'meta' | '__typename' > { id: string; customerId: string; diff --git a/packages/core/types/src/lib/interfaces/entities/index.ts b/packages/core/types/src/lib/interfaces/entities/index.ts index 1ce0b5f2..638f2499 100644 --- a/packages/core/types/src/lib/interfaces/entities/index.ts +++ b/packages/core/types/src/lib/interfaces/entities/index.ts @@ -7,4 +7,5 @@ export * from './order'; export * from './shop'; export * from './timezone'; export * from './product'; +export * from './role'; export * from './user'; diff --git a/packages/core/types/src/lib/interfaces/entities/invoice.ts b/packages/core/types/src/lib/interfaces/entities/invoice.ts index 168540d3..9e133ad1 100644 --- a/packages/core/types/src/lib/interfaces/entities/invoice.ts +++ b/packages/core/types/src/lib/interfaces/entities/invoice.ts @@ -5,7 +5,7 @@ import { IMeta } from './meta'; export interface IInvoice extends Omit< IoRestorecommerceInvoiceInvoice, - 'id' | 'customerId' | 'shopId' | 'userId' | 'meta' + 'id' | 'customerId' | 'shopId' | 'userId' | 'meta' | '__typename' > { id: string; customerId: string; diff --git a/packages/core/types/src/lib/interfaces/entities/locale.ts b/packages/core/types/src/lib/interfaces/entities/locale.ts index bc9f2571..638bd625 100644 --- a/packages/core/types/src/lib/interfaces/entities/locale.ts +++ b/packages/core/types/src/lib/interfaces/entities/locale.ts @@ -5,7 +5,7 @@ import { IMeta } from './meta'; export interface ILocale extends Omit< IoRestorecommerceLocaleLocale, - 'id' | 'description' | 'value' | 'meta' + 'id' | 'description' | 'value' | 'meta' | '__typename' > { id: string; description: string; diff --git a/packages/core/types/src/lib/interfaces/entities/meta.ts b/packages/core/types/src/lib/interfaces/entities/meta.ts index 01d68fca..a5637696 100644 --- a/packages/core/types/src/lib/interfaces/entities/meta.ts +++ b/packages/core/types/src/lib/interfaces/entities/meta.ts @@ -3,7 +3,7 @@ import { IoRestorecommerceMetaMeta } from '@console-core/graphql'; export interface IMeta extends Omit< IoRestorecommerceMetaMeta, - 'created' | 'createdBy' | 'modified' | 'modifiedBy' + 'created' | 'createdBy' | 'modified' | 'modifiedBy' | '__typename' > { id: string; created: string; diff --git a/packages/core/types/src/lib/interfaces/entities/order.ts b/packages/core/types/src/lib/interfaces/entities/order.ts index eac7b3c0..4b53379f 100644 --- a/packages/core/types/src/lib/interfaces/entities/order.ts +++ b/packages/core/types/src/lib/interfaces/entities/order.ts @@ -18,6 +18,7 @@ export interface IOrder | 'customerOrderNr' | 'orderState' | 'meta' + | '__typename' > { id: string; customer: IoRestorecommerceCustomerCustomer; diff --git a/packages/core/types/src/lib/interfaces/entities/product.ts b/packages/core/types/src/lib/interfaces/entities/product.ts index 14dab11e..cd5e4ebe 100644 --- a/packages/core/types/src/lib/interfaces/entities/product.ts +++ b/packages/core/types/src/lib/interfaces/entities/product.ts @@ -8,7 +8,7 @@ import { IMeta } from './meta'; export interface IProductItem extends Omit< IoRestorecommerceProductIndividualProduct, - 'name' | 'description' + 'name' | 'description' | '__typename' > { name: string; description: string; @@ -17,7 +17,7 @@ export interface IProductItem export interface IProduct extends Omit< IoRestorecommerceProductProduct, - 'id' | 'active' | 'product' | 'meta' + 'id' | 'active' | 'product' | 'meta' | '__typename' > { id: string; active: boolean; diff --git a/packages/core/types/src/lib/interfaces/entities/role.ts b/packages/core/types/src/lib/interfaces/entities/role.ts new file mode 100644 index 00000000..fdc4514c --- /dev/null +++ b/packages/core/types/src/lib/interfaces/entities/role.ts @@ -0,0 +1,15 @@ +import { IoRestorecommerceRoleRole } from '@console-core/graphql'; + +import { IMeta } from './meta'; + +export interface IRole + extends Omit< + IoRestorecommerceRoleRole, + 'id' | 'name' | 'description' | 'meta' | '__typename' + > { + id: string; + name: string; + description: string; + assignableByRoles: string[]; + meta: IMeta; +} diff --git a/packages/core/types/src/lib/interfaces/entities/shop.ts b/packages/core/types/src/lib/interfaces/entities/shop.ts index 5c00db7a..bb8652ca 100644 --- a/packages/core/types/src/lib/interfaces/entities/shop.ts +++ b/packages/core/types/src/lib/interfaces/entities/shop.ts @@ -3,7 +3,10 @@ import { IoRestorecommerceShopShop } from '@console-core/graphql'; import { IMeta } from './meta'; export interface IShop - extends Omit { + extends Omit< + IoRestorecommerceShopShop, + 'id' | 'name' | 'meta' | '__typename' + > { id: string; name: string; meta: IMeta; diff --git a/packages/core/types/src/lib/interfaces/entities/timezone.ts b/packages/core/types/src/lib/interfaces/entities/timezone.ts index 425154d5..b513fb3e 100644 --- a/packages/core/types/src/lib/interfaces/entities/timezone.ts +++ b/packages/core/types/src/lib/interfaces/entities/timezone.ts @@ -5,9 +5,10 @@ import { IMeta } from './meta'; export interface ITimezone extends Omit< IoRestorecommerceTimezoneTimezone, - 'id' | 'description' | 'meta' + 'id' | 'description' | 'meta' | '__typename' > { id: string; + value: string; description: string; meta: IMeta; } diff --git a/packages/core/types/src/lib/interfaces/entities/user.ts b/packages/core/types/src/lib/interfaces/entities/user.ts index dc5bd9de..74252628 100644 --- a/packages/core/types/src/lib/interfaces/entities/user.ts +++ b/packages/core/types/src/lib/interfaces/entities/user.ts @@ -1,18 +1,43 @@ -import { IoRestorecommerceUserUser } from '@console-core/graphql'; +import { + IoRestorecommerceUserUser, + IoRestorecommerceUserUserRole, +} from '@console-core/graphql'; import { IMeta } from './meta'; export interface IUser extends Omit< - IoRestorecommerceUserUser, - 'id' | 'email' | 'name' | 'firstName' | 'lastName' | 'meta' - > { + IoRestorecommerceUserUser, + | 'id' + | 'email' + | 'name' + | 'firstName' + | 'lastName' + | 'roleAssociations' + | 'meta' + | '__typename' + >, + Omit< + IoRestorecommerceUserUserRole, + | 'id' + | 'email' + | 'name' + | 'firstName' + | 'lastName' + | 'roleAssociations' + | 'meta' + | '__typename' + > { id: string; email: string; name: string; - fullName?: string; firstName: string; lastName: string; + fullName: string; + roleAssociations: Array<{ + id: string; + role: string; + }>; isSuperAdministrator: boolean; isAdministrator: boolean; isUser: boolean; diff --git a/packages/core/types/src/lib/interfaces/router.ts b/packages/core/types/src/lib/interfaces/router.ts index eeacd51f..ca46c339 100644 --- a/packages/core/types/src/lib/interfaces/router.ts +++ b/packages/core/types/src/lib/interfaces/router.ts @@ -120,7 +120,9 @@ export interface IRouterConstant { getLink: () => TRouterLink; children: { index: IRouterItem; - iam: IRouterItem; + create: IRouterItem; + view: IRouterItem; + edit: IRouterItem; }; }; addresses: { @@ -209,7 +211,9 @@ export interface IRouterConstant { getLink: () => TRouterLink; children: { index: IRouterItem; - roles: IRouterItem; + create: IRouterItem; + view: IRouterItem; + edit: IRouterItem; }; }; rules: { diff --git a/packages/core/types/src/lib/interfaces/state/iam.state.ts b/packages/core/types/src/lib/interfaces/state/iam.state.ts new file mode 100644 index 00000000..a4be4f1d --- /dev/null +++ b/packages/core/types/src/lib/interfaces/state/iam.state.ts @@ -0,0 +1,9 @@ +import { EntityState } from '@ngrx/entity'; + +import { IUser } from '../entities'; + +import { IBaseStore } from './store.state'; + +export interface IIamState extends EntityState, IBaseStore { + selectedId: string | null; +} diff --git a/packages/core/types/src/lib/interfaces/state/index.ts b/packages/core/types/src/lib/interfaces/state/index.ts index fedcc4e9..82122a61 100644 --- a/packages/core/types/src/lib/interfaces/state/index.ts +++ b/packages/core/types/src/lib/interfaces/state/index.ts @@ -3,10 +3,12 @@ export * from './app.state'; export * from './authn.state'; export * from './country.state'; export * from './fulfillment.state'; +export * from './iam.state'; export * from './invoice.state'; export * from './locale.state'; export * from './order.state'; export * from './product.state'; +export * from './role.state'; export * from './router.state'; export * from './store.state'; export * from './timezone.state'; diff --git a/packages/core/types/src/lib/interfaces/state/role.state.ts b/packages/core/types/src/lib/interfaces/state/role.state.ts new file mode 100644 index 00000000..781605ea --- /dev/null +++ b/packages/core/types/src/lib/interfaces/state/role.state.ts @@ -0,0 +1,9 @@ +import { EntityState } from '@ngrx/entity'; + +import { IRole } from '../entities'; + +import { IBaseStore } from './store.state'; + +export interface IRoleState extends EntityState, IBaseStore { + selectedId: string | null; +} diff --git a/packages/core/types/src/lib/interfaces/store.ts b/packages/core/types/src/lib/interfaces/store.ts index fe9238a5..9426a007 100644 --- a/packages/core/types/src/lib/interfaces/store.ts +++ b/packages/core/types/src/lib/interfaces/store.ts @@ -5,10 +5,12 @@ export interface IStoreConstant { readonly authnState: 'authnStateV1'; readonly countryState: 'countryStateV1'; readonly fulfillmentState: 'fulfillmentStateV1'; + readonly iamState: 'iamStateV1'; readonly invoiceState: 'invoiceStateV1'; readonly localeState: 'localeStateV1'; readonly orderState: 'orderStateV1'; readonly productState: 'productStateV1'; + readonly roleState: 'roleStateV1'; readonly routerState: 'routerStateV1'; readonly timezoneState: 'timezoneStateV1'; }; diff --git a/packages/modules/account/src/lib/components/preferences/preferences.component.ts b/packages/modules/account/src/lib/components/preferences/preferences.component.ts index a13d01d3..6561c01b 100644 --- a/packages/modules/account/src/lib/components/preferences/preferences.component.ts +++ b/packages/modules/account/src/lib/components/preferences/preferences.component.ts @@ -1,6 +1,6 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; import { combineLatest } from 'rxjs'; -import { startWith, tap } from 'rxjs/operators'; +import { tap } from 'rxjs/operators'; import { VCLFormFieldSchemaRoot } from '@vcl/ng-vcl'; @@ -36,11 +36,6 @@ export class PreferencesComponent implements OnInit { locales: this.localeFacade.all$, timezones: this.timezoneFacade.all$, }).pipe( - startWith({ - user: null, - locales: [], - timezones: [], - }), tap(({ user, locales, timezones }) => { this.localizationFormSchema = buildLocalizationDataSchema({ user, diff --git a/packages/modules/account/src/lib/jss-forms/localization-data.jss-form.ts b/packages/modules/account/src/lib/jss-forms/localization-data.jss-form.ts index db9e09f8..30b36d81 100644 --- a/packages/modules/account/src/lib/jss-forms/localization-data.jss-form.ts +++ b/packages/modules/account/src/lib/jss-forms/localization-data.jss-form.ts @@ -20,13 +20,13 @@ export const buildLocalizationDataSchema = ({ fields: [ { name: 'localeId', - label: 'Language', + label: 'Locale', type: 'select', defaultValue: user?.localeId, validators: [Validators.required], params: { options: locales.map((locale) => ({ - label: locale.id, + label: locale.value, sublabel: locale.description, value: locale.id, })), @@ -47,7 +47,7 @@ export const buildLocalizationDataSchema = ({ validators: [Validators.required], params: { options: timezones.map((timezone) => ({ - label: timezone.id, + label: timezone.value, sublabel: timezone.description, value: timezone.id, })), diff --git a/packages/modules/fulfillment/src/lib/components/fulfillment-create.component.ts b/packages/modules/fulfillment/src/lib/components/fulfillment-create.component.ts index dc0fe1ef..c7a16d38 100644 --- a/packages/modules/fulfillment/src/lib/components/fulfillment-create.component.ts +++ b/packages/modules/fulfillment/src/lib/components/fulfillment-create.component.ts @@ -5,7 +5,7 @@ import { VCLFormFieldSchemaRoot } from '@vcl/ng-vcl'; import { FulfillmentFacade } from '@console-core/state'; -import { buildFulfillmentSchema } from './jss-forms'; +import { buildFulfillmentSchema } from '../jss-forms'; @Component({ selector: 'app-module-fulfillment-create', diff --git a/packages/modules/fulfillment/src/lib/components/fulfillment-edit.component.ts b/packages/modules/fulfillment/src/lib/components/fulfillment-edit.component.ts index 5131a1dc..94ab56f0 100644 --- a/packages/modules/fulfillment/src/lib/components/fulfillment-edit.component.ts +++ b/packages/modules/fulfillment/src/lib/components/fulfillment-edit.component.ts @@ -12,7 +12,7 @@ import { filterEmptyAndNullishAndUndefined, } from '@console-core/state'; -import { buildFulfillmentSchema } from './jss-forms'; +import { buildFulfillmentSchema } from '../jss-forms'; @Component({ selector: 'app-module-fulfillment-edit', diff --git a/packages/modules/ui/src/lib/components/organisms/fulfillment/fulfillment-view.component.ts b/packages/modules/fulfillment/src/lib/components/fulfillment-view-details.component.ts similarity index 59% rename from packages/modules/ui/src/lib/components/organisms/fulfillment/fulfillment-view.component.ts rename to packages/modules/fulfillment/src/lib/components/fulfillment-view-details.component.ts index 8bb9ee5f..e70b7541 100644 --- a/packages/modules/ui/src/lib/components/organisms/fulfillment/fulfillment-view.component.ts +++ b/packages/modules/fulfillment/src/lib/components/fulfillment-view-details.component.ts @@ -3,10 +3,15 @@ import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; import { IFulfillment } from '@console-core/types'; @Component({ - selector: 'rc-fulfillment-view', - templateUrl: './fulfillment-view.component.html', + selector: 'app-module-fulfillment-view-details', + template: ` +
+      {{ fulfillment | json }}
+    
+ `, changeDetection: ChangeDetectionStrategy.OnPush, }) -export class RcFulfillmentViewComponent { +export class FulfillmentViewDetailsComponent { @Input({ required: true }) fulfillment!: IFulfillment; } diff --git a/packages/modules/fulfillment/src/lib/components/fulfillment-view.component.ts b/packages/modules/fulfillment/src/lib/components/fulfillment-view.component.ts index 741063db..adf50579 100644 --- a/packages/modules/fulfillment/src/lib/components/fulfillment-view.component.ts +++ b/packages/modules/fulfillment/src/lib/components/fulfillment-view.component.ts @@ -14,7 +14,7 @@ import { selector: 'app-module-fulfillment-view', template: ` - + `, changeDetection: ChangeDetectionStrategy.OnPush, diff --git a/packages/modules/fulfillment/src/lib/components/jss-forms/fulfillment.jss-form.ts b/packages/modules/fulfillment/src/lib/jss-forms/fulfillment.jss-form.ts similarity index 100% rename from packages/modules/fulfillment/src/lib/components/jss-forms/fulfillment.jss-form.ts rename to packages/modules/fulfillment/src/lib/jss-forms/fulfillment.jss-form.ts diff --git a/packages/modules/fulfillment/src/lib/components/jss-forms/index.ts b/packages/modules/fulfillment/src/lib/jss-forms/index.ts similarity index 100% rename from packages/modules/fulfillment/src/lib/components/jss-forms/index.ts rename to packages/modules/fulfillment/src/lib/jss-forms/index.ts diff --git a/packages/modules/fulfillment/src/lib/modules-fulfillment.module.ts b/packages/modules/fulfillment/src/lib/modules-fulfillment.module.ts index 8b9aa9e0..0a560d83 100644 --- a/packages/modules/fulfillment/src/lib/modules-fulfillment.module.ts +++ b/packages/modules/fulfillment/src/lib/modules-fulfillment.module.ts @@ -6,17 +6,19 @@ import { ModulesUiModule } from '@console-modules/ui'; import { FulfillmentCreateComponent } from './components/fulfillment-create.component'; import { FulfillmentEditComponent } from './components/fulfillment-edit.component'; import { FulfillmentIndexComponent } from './components/fulfillment-index.component'; +import { FulfillmentViewDetailsComponent } from './components/fulfillment-view-details.component'; import { FulfillmentViewComponent } from './components/fulfillment-view.component'; import { FulfillmentTemplateComponent } from './components/template/fulfillment-template.component'; import { modulesFulfillmentRoutes } from './lib.routes'; @NgModule({ declarations: [ - FulfillmentTemplateComponent, FulfillmentIndexComponent, FulfillmentCreateComponent, FulfillmentEditComponent, FulfillmentViewComponent, + FulfillmentViewDetailsComponent, + FulfillmentTemplateComponent, ], imports: [ ModulesUiModule.forChild(), diff --git a/packages/modules/invoice/src/lib/components/invoice-create.component.ts b/packages/modules/invoice/src/lib/components/invoice-create.component.ts index d802c293..4f0891a8 100644 --- a/packages/modules/invoice/src/lib/components/invoice-create.component.ts +++ b/packages/modules/invoice/src/lib/components/invoice-create.component.ts @@ -5,7 +5,7 @@ import { VCLFormFieldSchemaRoot } from '@vcl/ng-vcl'; import { InvoiceFacade } from '@console-core/state'; -import { buildInvoiceSchema } from './jss-forms'; +import { buildInvoiceSchema } from '../jss-forms'; @Component({ selector: 'app-module-invoice-create', diff --git a/packages/modules/invoice/src/lib/components/invoice-edit.component.ts b/packages/modules/invoice/src/lib/components/invoice-edit.component.ts index e944efdf..c9c3b55c 100644 --- a/packages/modules/invoice/src/lib/components/invoice-edit.component.ts +++ b/packages/modules/invoice/src/lib/components/invoice-edit.component.ts @@ -12,7 +12,7 @@ import { filterEmptyAndNullishAndUndefined, } from '@console-core/state'; -import { buildInvoiceSchema } from './jss-forms'; +import { buildInvoiceSchema } from '../jss-forms'; @Component({ selector: 'app-module-invoice-edit', diff --git a/packages/modules/ui/src/lib/components/organisms/invoice/invoice-view.component.ts b/packages/modules/invoice/src/lib/components/invoice-view-details.component.ts similarity index 60% rename from packages/modules/ui/src/lib/components/organisms/invoice/invoice-view.component.ts rename to packages/modules/invoice/src/lib/components/invoice-view-details.component.ts index 0b6985a5..4917d4f1 100644 --- a/packages/modules/ui/src/lib/components/organisms/invoice/invoice-view.component.ts +++ b/packages/modules/invoice/src/lib/components/invoice-view-details.component.ts @@ -3,10 +3,15 @@ import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; import { IInvoice } from '@console-core/types'; @Component({ - selector: 'rc-invoice-view', - templateUrl: './invoice-view.component.html', + selector: 'app-module-invoice-view-details', + template: ` +
+      {{ invoice | json }}
+    
+ `, changeDetection: ChangeDetectionStrategy.OnPush, }) -export class RcInvoiceViewComponent { +export class InvoiceViewDetailsComponent { @Input({ required: true }) invoice!: IInvoice; } diff --git a/packages/modules/invoice/src/lib/components/invoice-view.component.ts b/packages/modules/invoice/src/lib/components/invoice-view.component.ts index bb38444a..24c8c4ee 100644 --- a/packages/modules/invoice/src/lib/components/invoice-view.component.ts +++ b/packages/modules/invoice/src/lib/components/invoice-view.component.ts @@ -14,7 +14,7 @@ import { selector: 'app-module-invoice-view', template: ` - + `, changeDetection: ChangeDetectionStrategy.OnPush, diff --git a/packages/modules/invoice/src/lib/components/jss-forms/index.ts b/packages/modules/invoice/src/lib/jss-forms/index.ts similarity index 100% rename from packages/modules/invoice/src/lib/components/jss-forms/index.ts rename to packages/modules/invoice/src/lib/jss-forms/index.ts diff --git a/packages/modules/invoice/src/lib/components/jss-forms/invoice.jss-form.ts b/packages/modules/invoice/src/lib/jss-forms/invoice.jss-form.ts similarity index 100% rename from packages/modules/invoice/src/lib/components/jss-forms/invoice.jss-form.ts rename to packages/modules/invoice/src/lib/jss-forms/invoice.jss-form.ts diff --git a/packages/modules/invoice/src/lib/modules-invoice.module.ts b/packages/modules/invoice/src/lib/modules-invoice.module.ts index cc049eea..4e7ed151 100644 --- a/packages/modules/invoice/src/lib/modules-invoice.module.ts +++ b/packages/modules/invoice/src/lib/modules-invoice.module.ts @@ -6,17 +6,19 @@ import { ModulesUiModule } from '@console-modules/ui'; import { InvoiceCreateComponent } from './components/invoice-create.component'; import { InvoiceEditComponent } from './components/invoice-edit.component'; import { InvoiceIndexComponent } from './components/invoice-index.component'; +import { InvoiceViewDetailsComponent } from './components/invoice-view-details.component'; import { InvoiceViewComponent } from './components/invoice-view.component'; import { InvoiceTemplateComponent } from './components/template/invoice-template.component'; import { modulesInvoiceRoutes } from './lib.routes'; @NgModule({ declarations: [ - InvoiceTemplateComponent, InvoiceIndexComponent, InvoiceCreateComponent, InvoiceEditComponent, InvoiceViewComponent, + InvoiceViewDetailsComponent, + InvoiceTemplateComponent, ], imports: [ ModulesUiModule.forChild(), diff --git a/packages/modules/management/src/lib/components/country/country-index.component.ts b/packages/modules/management/src/lib/components/country/country-index.component.ts index c9c56df6..f7c8b327 100644 --- a/packages/modules/management/src/lib/components/country/country-index.component.ts +++ b/packages/modules/management/src/lib/components/country/country-index.component.ts @@ -10,7 +10,7 @@ import { } from '@console-core/state'; @Component({ - selector: 'app-module-management-country', + selector: 'app-module-management-country-index', template: `

Countries

diff --git a/packages/modules/ui/src/lib/components/organisms/management/country/country-view.component.ts b/packages/modules/management/src/lib/components/country/country-view-details.component.ts similarity index 92% rename from packages/modules/ui/src/lib/components/organisms/management/country/country-view.component.ts rename to packages/modules/management/src/lib/components/country/country-view-details.component.ts index 1d681ca1..2c45fc6c 100644 --- a/packages/modules/ui/src/lib/components/organisms/management/country/country-view.component.ts +++ b/packages/modules/management/src/lib/components/country/country-view-details.component.ts @@ -3,7 +3,7 @@ import { Component, Input } from '@angular/core'; import { ICountry } from '@console-core/types'; @Component({ - selector: 'rc-management-country-view', + selector: 'app-module-management-country-view-details', template: `
Data
@@ -36,6 +36,6 @@ import { ICountry } from '@console-core/types';
`, }) -export class RcCountryViewComponent { +export class CountryViewDetailsComponent { @Input({ required: true }) country!: ICountry; } diff --git a/packages/modules/management/src/lib/components/country/country-view.component.ts b/packages/modules/management/src/lib/components/country/country-view.component.ts index fb874eff..20f61ec0 100644 --- a/packages/modules/management/src/lib/components/country/country-view.component.ts +++ b/packages/modules/management/src/lib/components/country/country-view.component.ts @@ -14,7 +14,7 @@ import { selector: 'app-module-management-country-view', template: ` - + `, changeDetection: ChangeDetectionStrategy.OnPush, @@ -26,6 +26,7 @@ export class CountryViewComponent { filterEmptyAndNullishAndUndefined(), tap((id) => { this.countryFacade.setSelectedId(id); + this.countryFacade.readOneById({ id }); }) ), country: this.countryFacade.selected$.pipe( diff --git a/packages/modules/management/src/lib/components/country/country.module.ts b/packages/modules/management/src/lib/components/country/country.module.ts index db25284f..bb8a16e7 100644 --- a/packages/modules/management/src/lib/components/country/country.module.ts +++ b/packages/modules/management/src/lib/components/country/country.module.ts @@ -7,6 +7,7 @@ import { ModulesUiModule } from '@console-modules/ui'; import { CountryCreateComponent } from './country-create.component'; import { CountryEditComponent } from './country-edit.component'; import { CountryIndexComponent } from './country-index.component'; +import { CountryViewDetailsComponent } from './country-view-details.component'; import { CountryViewComponent } from './country-view.component'; import { CountryTemplateComponent } from './template/country-template.component'; @@ -59,11 +60,12 @@ const routes: Routes = [ @NgModule({ declarations: [ - CountryTemplateComponent, CountryIndexComponent, CountryCreateComponent, CountryEditComponent, CountryViewComponent, + CountryViewDetailsComponent, + CountryTemplateComponent, ], imports: [ModulesUiModule.forChild(), RouterModule.forChild(routes)], }) diff --git a/packages/modules/management/src/lib/components/country/jss-forms/country.jss-form.ts b/packages/modules/management/src/lib/components/country/jss-forms/country.jss-form.ts index f5def852..c2498089 100644 --- a/packages/modules/management/src/lib/components/country/jss-forms/country.jss-form.ts +++ b/packages/modules/management/src/lib/components/country/jss-forms/country.jss-form.ts @@ -8,9 +8,9 @@ interface ISchemaOptions { country?: ICountry; } -export const buildCountrySchema = ( - options: ISchemaOptions -): VCLFormFieldSchemaRoot => { +export const buildCountrySchema = ({ + country, +}: ISchemaOptions): VCLFormFieldSchemaRoot => { return { type: 'form', fields: [ @@ -18,7 +18,7 @@ export const buildCountrySchema = ( name: 'name', label: 'Name', type: 'input', - ...(options.country ? { defaultValue: options.country.name } : {}), + ...(country ? { defaultValue: country.name } : {}), validators: [Validators.required], params: {}, hints: [ @@ -33,9 +33,7 @@ export const buildCountrySchema = ( name: 'geographicalName', label: 'Geographical name', type: 'input', - ...(options.country - ? { defaultValue: options.country.geographicalName } - : {}), + ...(country ? { defaultValue: country.geographicalName } : {}), validators: [Validators.required], params: {}, hints: [ @@ -50,9 +48,7 @@ export const buildCountrySchema = ( name: 'countryCode', label: 'Country code', type: 'input', - ...(options.country - ? { defaultValue: options.country.countryCode } - : {}), + ...(country ? { defaultValue: country.countryCode } : {}), validators: [Validators.required, Validators.pattern('^[A-Z]{2}$')], params: {}, hints: [ @@ -72,19 +68,13 @@ export const buildCountrySchema = ( name: 'economicAreas', label: 'Economic areas', type: 'select', - ...(options.country - ? { defaultValue: options.country.economicAreas || [] } - : {}), + ...(country ? { defaultValue: country.economicAreas || [] } : {}), validators: [Validators.required], params: { placeholder: 'Select economic areas', selectionMode: 'multiple', clearable: true, search: false, - // emptyComponent: { - // component: emptyComponent, - // data: 'No economic areas found!', - // }, // TODO: Load from API options: [ { diff --git a/packages/modules/management/src/lib/components/iam/iam-create.component.ts b/packages/modules/management/src/lib/components/iam/iam-create.component.ts new file mode 100644 index 00000000..520f0a20 --- /dev/null +++ b/packages/modules/management/src/lib/components/iam/iam-create.component.ts @@ -0,0 +1,57 @@ +import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; +import { combineLatest } from 'rxjs'; +import { tap } from 'rxjs/operators'; + +import { VCLFormFieldSchemaRoot } from '@vcl/ng-vcl'; + +import { + IamFacade, + LocaleFacade, + RoleFacade, + TimezoneFacade, +} from '@console-core/state'; + +import { buildUserSchema } from './jss-forms'; + +@Component({ + selector: 'app-module-management-iam-create', + template: ` + +
+ +
+
+ `, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class IamCreateComponent implements OnInit { + schema!: VCLFormFieldSchemaRoot; // = buildUserSchema({}); + create = this.iamFacade.create; + + readonly vm$ = combineLatest({ + user: this.iamFacade.selected$, + locales: this.localeFacade.all$, + timezones: this.timezoneFacade.all$, + roles: this.roleFacade.all$, + }).pipe( + tap(({ user, locales, timezones, roles }) => { + this.schema = buildUserSchema({ user, locales, timezones, roles }); + }) + ); + + constructor( + private readonly iamFacade: IamFacade, + private readonly localeFacade: LocaleFacade, + private readonly timezoneFacade: TimezoneFacade, + private readonly roleFacade: RoleFacade + ) {} + + ngOnInit(): void { + this.localeFacade.read({}); + this.timezoneFacade.read({}); + this.roleFacade.read({}); + } +} diff --git a/packages/modules/management/src/lib/components/iam/iam-edit.component.ts b/packages/modules/management/src/lib/components/iam/iam-edit.component.ts new file mode 100644 index 00000000..66cddd7d --- /dev/null +++ b/packages/modules/management/src/lib/components/iam/iam-edit.component.ts @@ -0,0 +1,82 @@ +import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; +import { combineLatest } from 'rxjs'; +import { map, tap } from 'rxjs/operators'; + +import { VCLFormFieldSchemaRoot } from '@vcl/ng-vcl'; + +import { ROUTER } from '@console-core/config'; +import { + IamFacade, + LocaleFacade, + RoleFacade, + RouterFacade, + TimezoneFacade, + filterEmptyAndNullishAndUndefined, +} from '@console-core/state'; + +import { buildUserSchema } from './jss-forms'; + +@Component({ + selector: 'app-module-management-iam-edit', + template: ` + +
+ +
+
+ `, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class IamEditComponent implements OnInit { + schema!: VCLFormFieldSchemaRoot; + update = this.iamFacade.update; + id: string | null = null; + + readonly vm$ = combineLatest({ + id: this.routerFacade.params$.pipe( + map(({ id }) => id), + tap((id) => { + this.id = id; + this.iamFacade.setSelectedId(id); + }) + ), + user: this.iamFacade.selected$.pipe( + tap((user) => { + if (!user) { + this.iamFacade.setSelectedId(null); + this.router.navigate( + ROUTER.pages.main.children.management.children.iam.children.index.getLink() + ); + } + }), + filterEmptyAndNullishAndUndefined() + ), + locales: this.localeFacade.all$, + timezones: this.timezoneFacade.all$, + roles: this.roleFacade.all$, + }).pipe( + tap(({ user, locales, timezones, roles }) => { + this.schema = buildUserSchema({ user, locales, timezones, roles }); + }) + ); + + constructor( + private readonly router: Router, + private readonly routerFacade: RouterFacade, + private readonly iamFacade: IamFacade, + private readonly localeFacade: LocaleFacade, + private readonly timezoneFacade: TimezoneFacade, + private readonly roleFacade: RoleFacade + ) {} + + ngOnInit(): void { + this.localeFacade.read({}); + this.timezoneFacade.read({}); + this.roleFacade.read({}); + } +} diff --git a/packages/modules/management/src/lib/components/iam/iam-index.component.ts b/packages/modules/management/src/lib/components/iam/iam-index.component.ts new file mode 100644 index 00000000..45212655 --- /dev/null +++ b/packages/modules/management/src/lib/components/iam/iam-index.component.ts @@ -0,0 +1,39 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { Router } from '@angular/router'; +import { combineLatest } from 'rxjs'; +import { tap } from 'rxjs/operators'; + +import { ROUTER } from '@console-core/config'; +import { + IamFacade, + filterEmptyAndNullishAndUndefined, +} from '@console-core/state'; + +@Component({ + selector: 'app-module-management-iam-index', + template: ` + +

IAM

+
+ `, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class IamIndexComponent { + readonly vm$ = combineLatest({ + selectedUserId: this.IamFacade.selectedId$.pipe( + filterEmptyAndNullishAndUndefined(), + tap((id) => { + this.router.navigate( + ROUTER.pages.main.children.management.children.iam.children.view.getLink( + { id } + ) + ); + }) + ), + }); + + constructor( + private readonly router: Router, + private readonly IamFacade: IamFacade + ) {} +} diff --git a/packages/modules/management/src/lib/components/iam/iam-view-details.component.ts b/packages/modules/management/src/lib/components/iam/iam-view-details.component.ts new file mode 100644 index 00000000..e6ab040d --- /dev/null +++ b/packages/modules/management/src/lib/components/iam/iam-view-details.component.ts @@ -0,0 +1,90 @@ +import { Component, Input } from '@angular/core'; + +import { UserService } from '@console-core/state'; +import { IUser } from '@console-core/types'; + +@Component({ + selector: 'app-module-management-iam-view-details', + template: ` +
+
Data
+
    +
  • +
    First name:
    +
    + {{ user.firstName }} +
    +
  • + +
  • +
    Last name:
    +
    + {{ user.lastName }} +
    +
  • + +
  • +
    Username:
    +
    + {{ user.name }} +
    +
  • + +
  • +
    Email:
    + +
  • + +
  • +
    Active:
    +
    + + {{ user.active ? 'Yes' : 'No' }} + +
    +
  • + +
  • +
    Locale:
    +
    + {{ user.locale?.description }} + + {{ user.locale?.value }} + +
    +
  • + +
  • +
    Timezone:
    +
    + {{ user.timezone?.description }} + + {{ user.timezone?.value }} + +
    +
  • + +
  • +
    Roles:
    +
    + {{ getRoleAssociations(user) }} +
    +
  • +
+
+ `, +}) +export class IamViewDetailsComponent { + @Input({ required: true }) user!: IUser; + + constructor(private readonly userService: UserService) {} + + getRoleAssociations(user: IUser): string { + return this.userService.getRoleAssociations(user); + } +} diff --git a/packages/modules/management/src/lib/components/iam/iam-view.component.ts b/packages/modules/management/src/lib/components/iam/iam-view.component.ts new file mode 100644 index 00000000..79246e6e --- /dev/null +++ b/packages/modules/management/src/lib/components/iam/iam-view.component.ts @@ -0,0 +1,50 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { Router } from '@angular/router'; +import { combineLatest } from 'rxjs'; +import { map, tap } from 'rxjs/operators'; + +import { ROUTER } from '@console-core/config'; +import { + IamFacade, + RouterFacade, + filterEmptyAndNullishAndUndefined, +} from '@console-core/state'; + +@Component({ + selector: 'app-module-management-iam-view', + template: ` + + + + `, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class IamViewComponent { + readonly vm$ = combineLatest({ + id: this.routerFacade.params$.pipe( + map(({ id }) => id), + filterEmptyAndNullishAndUndefined(), + tap((id) => { + this.iamFacade.setSelectedId(id); + this.iamFacade.readOneById({ id }); + }) + ), + user: this.iamFacade.selected$.pipe( + tap((user) => { + if (!user) { + this.iamFacade.setSelectedId(null); + this.router.navigate( + ROUTER.pages.main.children.management.children.iam.children.index.getLink() + ); + } + }), + filterEmptyAndNullishAndUndefined() + ), + }); + + constructor( + private readonly router: Router, + private readonly routerFacade: RouterFacade, + private readonly iamFacade: IamFacade + ) {} +} diff --git a/packages/modules/management/src/lib/components/iam/iam.component.ts b/packages/modules/management/src/lib/components/iam/iam.component.ts deleted file mode 100644 index 099f019b..00000000 --- a/packages/modules/management/src/lib/components/iam/iam.component.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; -import { map } from 'rxjs/operators'; - -@Component({ - selector: 'app-module-management-iam', - template: ` -
- -

User: {{ id }}

-
- - -

IAM

-
-
- `, - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class IamComponent { - id$ = this.route.params.pipe(map((params) => params['id'])); - - constructor(private readonly route: ActivatedRoute) {} -} diff --git a/packages/modules/management/src/lib/components/iam/iam.module.ts b/packages/modules/management/src/lib/components/iam/iam.module.ts index e5cbb58d..7fb7f588 100644 --- a/packages/modules/management/src/lib/components/iam/iam.module.ts +++ b/packages/modules/management/src/lib/components/iam/iam.module.ts @@ -4,22 +4,69 @@ import { RouterModule, Routes } from '@angular/router'; import { ROUTER } from '@console-core/config'; import { ModulesUiModule } from '@console-modules/ui'; -import { IamComponent } from './iam.component'; +import { IamCreateComponent } from './iam-create.component'; +import { IamEditComponent } from './iam-edit.component'; +import { IamIndexComponent } from './iam-index.component'; +import { IamViewDetailsComponent } from './iam-view-details.component'; +import { IamViewComponent } from './iam-view.component'; +import { IamTemplateComponent } from './template/iam-template.component'; const routes: Routes = [ { - path: ROUTER.pages.main.children.management.children.iam.children.index - .path, - component: IamComponent, - }, - { - path: ROUTER.pages.main.children.management.children.iam.children.iam.path, - component: IamComponent, + path: '', + component: IamTemplateComponent, + children: [ + { + path: ROUTER.pages.main.children.management.children.iam.children.index + .path, + component: IamIndexComponent, + title: + ROUTER.pages.main.children.management.children.iam.children.index + .title, + }, + { + path: ROUTER.pages.main.children.management.children.iam.children.view + .path, + component: IamViewComponent, + title: + ROUTER.pages.main.children.management.children.iam.children.view + .title, + }, + { + path: ROUTER.pages.main.children.management.children.iam.children.create + .path, + component: IamCreateComponent, + title: + ROUTER.pages.main.children.management.children.iam.children.create + .title, + }, + { + path: ROUTER.pages.main.children.management.children.iam.children.edit + .path, + component: IamEditComponent, + title: + ROUTER.pages.main.children.management.children.iam.children.edit + .title, + }, + { + path: '**', + redirectTo: + ROUTER.pages.main.children.management.children.iam.children.index + .path, + }, + ], }, ]; @NgModule({ - declarations: [IamComponent], + declarations: [ + IamCreateComponent, + IamEditComponent, + IamIndexComponent, + IamViewComponent, + IamViewDetailsComponent, + IamTemplateComponent, + ], imports: [ModulesUiModule.forChild(), RouterModule.forChild(routes)], }) export class IamModule {} diff --git a/packages/modules/management/src/lib/components/iam/jss-forms/iam.jss-form.ts b/packages/modules/management/src/lib/components/iam/jss-forms/iam.jss-form.ts new file mode 100644 index 00000000..07d05e96 --- /dev/null +++ b/packages/modules/management/src/lib/components/iam/jss-forms/iam.jss-form.ts @@ -0,0 +1,224 @@ +import { Validators } from '@angular/forms'; + +import { VCLFormFieldSchemaRoot } from '@vcl/ng-vcl'; + +import { ILocale, IRole, ITimezone, IUser } from '@console-core/types'; + +interface ISchemaOptions { + user: IUser | null; + locales: ILocale[]; + timezones: ITimezone[]; + roles: IRole[]; +} + +export const buildUserSchema = ({ + user, + locales, + timezones, + roles, +}: ISchemaOptions): VCLFormFieldSchemaRoot => { + return { + type: 'form', + fields: [ + { + name: 'firstName', + label: 'First name', + type: 'input', + ...(user ? { defaultValue: user.firstName } : {}), + validators: [Validators.required], + params: {}, + hints: [ + { + type: 'error', + error: 'required', + message: 'This field is required.', + }, + ], + }, + { + name: 'lastName', + label: 'Last name', + type: 'input', + ...(user ? { defaultValue: user.lastName } : {}), + validators: [Validators.required], + params: {}, + hints: [ + { + type: 'error', + error: 'required', + message: 'This field is required.', + }, + ], + }, + { + name: 'name', + label: 'Username', + type: 'input', + ...(user ? { defaultValue: user.name } : {}), + validators: [Validators.required], + params: {}, + hints: [ + { + type: 'error', + error: 'required', + message: 'This field is required.', + }, + ], + }, + { + name: 'email', + label: 'Email', + type: 'input', + ...(user ? { defaultValue: user.email } : {}), + validators: [Validators.required, Validators.email], + params: {}, + hints: [ + { + type: 'error', + error: 'required', + message: 'This field is required.', + }, + { + type: 'error', + error: 'email', + message: 'This field should be a valid email address.', + }, + ], + }, + { + name: 'active', + label: 'Active', + type: 'checkbox', + ...(user ? { defaultValue: user.active } : { defaultValue: true }), + validators: [], + params: {}, + hints: [], + }, + { + name: 'localeId', + label: 'Locale', + type: 'select', + ...(user ? { defaultValue: user.localeId } : {}), + validators: [Validators.required], + params: { + options: locales.map((locale) => ({ + label: locale.value, + sublabel: locale.description, + value: locale.id, + })), + }, + hints: [ + { + type: 'error', + error: 'required', + message: 'This field is required.', + }, + ], + }, + { + name: 'timezoneId', + label: 'Timezone', + type: 'select', + ...(user ? { defaultValue: user.timezoneId } : {}), + validators: [Validators.required], + params: { + options: timezones.map((timezone) => ({ + label: timezone.value, + sublabel: timezone.description, + value: timezone.id, + })), + }, + hints: [ + { + type: 'error', + error: 'required', + message: 'This field is required.', + }, + ], + }, + + { + name: 'roleAssociations', + label: 'Role associations', + type: 'array', + initialFields: 1, + fieldLabel: (index) => `Role ${index + 1}`, + noFieldsLabel: 'No role associations', + field: { + name: 'roles', + type: 'object', + fields: [ + { + name: 'roles', + label: 'Role', + type: 'select', + // ...(user + // ? { + // defaultValue: user.roles?.map((role) => role.id), + // } + // : {}), + validators: [Validators.required], + params: { + placeholder: 'Select role', + selectionMode: 'single', + clearable: true, + search: false, + options: roles.map((role) => ({ + label: role.name, + sublabel: role.description, + value: role.id, + })), + }, + hints: [ + { + type: 'error', + error: 'required', + message: 'This field is required.', + }, + ], + }, + { + name: 'organizationId', + label: 'Organization', + type: 'input', + defaultValue: '', + validators: [Validators.required], + hints: [ + { + type: 'error', + error: 'required', + message: 'This field is required.', + }, + ], + }, + ], + }, + // TODO: Check to add validators + // validators: [Validators.required], + // params: {}, + // hints: [ + // { + // type: 'error', + // error: 'required', + // message: 'This field is required.', + // }, + // ], + }, + { + type: 'buttons', + buttons: [ + { + type: 'button', + label: 'Cancel', + action: 'reset', + class: 'transparent', + }, + { + type: 'submit', + label: 'Save', + }, + ], + }, + ], + }; +}; diff --git a/packages/modules/management/src/lib/components/iam/jss-forms/index.ts b/packages/modules/management/src/lib/components/iam/jss-forms/index.ts new file mode 100644 index 00000000..72d3ec7b --- /dev/null +++ b/packages/modules/management/src/lib/components/iam/jss-forms/index.ts @@ -0,0 +1 @@ +export * from './iam.jss-form'; diff --git a/packages/modules/management/src/lib/components/iam/template/iam-template.component.html b/packages/modules/management/src/lib/components/iam/template/iam-template.component.html new file mode 100644 index 00000000..ec855537 --- /dev/null +++ b/packages/modules/management/src/lib/components/iam/template/iam-template.component.html @@ -0,0 +1,64 @@ + + +
+ +
+
+ + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
diff --git a/packages/modules/management/src/lib/components/iam/template/iam-template.component.ts b/packages/modules/management/src/lib/components/iam/template/iam-template.component.ts new file mode 100644 index 00000000..5c28fffa --- /dev/null +++ b/packages/modules/management/src/lib/components/iam/template/iam-template.component.ts @@ -0,0 +1,144 @@ +import { + ChangeDetectionStrategy, + Component, + OnDestroy, + OnInit, +} from '@angular/core'; +import { BehaviorSubject, combineLatest } from 'rxjs'; +import { + map, + tap, + distinctUntilChanged, + debounceTime, + skip, +} from 'rxjs/operators'; +import { SubSink } from 'subsink'; + +import { ROUTER } from '@console-core/config'; +import { + IIoRestorecommerceResourcebaseReadRequest, + IoRestorecommerceResourcebaseSortSortOrder, +} from '@console-core/graphql'; +import { IamFacade, RouterFacade, UserService } from '@console-core/state'; +import { ICrudFeature, EUrlSegment, IUser } from '@console-core/types'; + +@Component({ + selector: 'app-module-management-iam-template', + templateUrl: './iam-template.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class IamTemplateComponent implements OnInit, OnDestroy { + ROUTER = ROUTER; + featureRouter = ROUTER.pages.main.children.management.children.iam.children; + + feature: Readonly = { + name: { + plural: 'Users', + singular: 'User', + }, + links: { + index: () => this.featureRouter.index.getLink(), + create: () => this.featureRouter.create.getLink(), + edit: (id: string | null) => + this.featureRouter.edit.getLink({ id: id ?? undefined }), + view: (id: string | null) => + this.featureRouter.view.getLink({ id: id ?? undefined }), + }, + }; + + queryVariables: IIoRestorecommerceResourcebaseReadRequest = { + sorts: [ + { + field: 'name', + order: IoRestorecommerceResourcebaseSortSortOrder.Ascending, + }, + ], + }; + + readonly triggerRead = new BehaviorSubject(null); + readonly triggerRead$ = this.triggerRead + .asObservable() + .pipe(tap(() => this.iamFacade.read(this.queryVariables))); + + readonly triggerSearch = new BehaviorSubject(''); + readonly triggerSearch$ = this.triggerSearch.asObservable().pipe( + skip(1), + debounceTime(300), + distinctUntilChanged(), + tap((value) => { + this.queryVariables = { + ...this.queryVariables, + search: { + caseSensitive: false, + search: value, + fields: ['name', 'first_name', 'last_name', 'email'], + }, + }; + this.iamFacade.read(this.queryVariables); + }) + ); + + readonly triggerSelectId = new BehaviorSubject(null); + readonly triggerSelectId$ = this.triggerSelectId + .asObservable() + .pipe(tap((id) => this.iamFacade.setSelectedId(id))); + + readonly triggerRemove = new BehaviorSubject(null); + readonly triggerRemove$ = this.triggerRemove.asObservable().pipe( + tap((id) => { + if (id === null) { + return; + } + this.iamFacade.remove({ id }); + }) + ); + + readonly urlSegment$ = this.routerFacade.url$.pipe( + map((url) => url.split('/').pop() as EUrlSegment), + distinctUntilChanged(), + tap((segment) => { + if ([EUrlSegment.Index, EUrlSegment.Create].includes(segment)) { + this.iamFacade.setSelectedId(null); + } + }), + debounceTime(10) + ); + + readonly vm$ = combineLatest({ + dataList: this.iamFacade.all$, + selectedUserId: this.iamFacade.selectedId$, + selectedUser: this.iamFacade.selected$, + urlSegment: this.urlSegment$, + triggerRead: this.triggerRead$, + triggerSelectId: this.triggerSelectId$, + triggerRemove: this.triggerRemove$, + }); + + private readonly subscriptions = new SubSink(); + + constructor( + private readonly iamFacade: IamFacade, + private readonly routerFacade: RouterFacade, + private readonly userService: UserService + ) {} + + ngOnInit(): void { + this.subscriptions.sink = this.triggerSearch$.subscribe(); + } + + ngOnDestroy(): void { + this.subscriptions.unsubscribe(); + } + + onSearch(value: string): void { + this.triggerSearch.next(value); + } + + getRoleAssociations(user: IUser): string { + return this.userService.getRoleAssociations(user); + } + + trackByFn(_: number, item: IUser) { + return item.id; + } +} diff --git a/packages/modules/management/src/lib/components/role/jss-forms/index.ts b/packages/modules/management/src/lib/components/role/jss-forms/index.ts new file mode 100644 index 00000000..1541d1ec --- /dev/null +++ b/packages/modules/management/src/lib/components/role/jss-forms/index.ts @@ -0,0 +1 @@ +export * from './role.jss-form'; diff --git a/packages/modules/management/src/lib/components/role/jss-forms/role.jss-form.ts b/packages/modules/management/src/lib/components/role/jss-forms/role.jss-form.ts new file mode 100644 index 00000000..efd406ac --- /dev/null +++ b/packages/modules/management/src/lib/components/role/jss-forms/role.jss-form.ts @@ -0,0 +1,89 @@ +import { Validators } from '@angular/forms'; + +import { VCLFormFieldSchemaRoot } from '@vcl/ng-vcl'; + +import { IRole } from '@console-core/types'; + +interface ISchemaOptions { + role?: IRole; + assignableByRoles?: string[]; +} + +export const buildRoleSchema = ({ + role, + assignableByRoles = [], +}: ISchemaOptions): VCLFormFieldSchemaRoot => { + return { + type: 'form', + fields: [ + { + name: 'name', + label: 'Name', + type: 'input', + ...(role ? { defaultValue: role.name } : {}), + validators: [Validators.required], + params: {}, + hints: [ + { + type: 'error', + error: 'required', + message: 'This field is required.', + }, + ], + }, + { + name: 'description', + label: 'Description', + type: 'input', + ...(role ? { defaultValue: role.description } : {}), + validators: [Validators.required], + params: {}, + hints: [ + { + type: 'error', + error: 'required', + message: 'This field is required.', + }, + ], + }, + { + name: 'assignableByRoles', + label: 'Assignable by roles', + type: 'select', + ...(role ? { defaultValue: role.assignableByRoles || [] } : {}), + validators: [Validators.required], + params: { + placeholder: 'Select assignable by roles', + selectionMode: 'multiple', + clearable: true, + search: false, + options: assignableByRoles + .map((id) => ({ label: id.replace(/-r-id/g, ' '), value: id })) + .sort((a, b) => a.label.localeCompare(b.label)), + }, + hints: [ + { + type: 'error', + error: 'required', + message: 'This field is required.', + }, + ], + }, + { + type: 'buttons', + buttons: [ + { + type: 'button', + label: 'Cancel', + action: 'reset', + class: 'transparent', + }, + { + type: 'submit', + label: 'Save', + }, + ], + }, + ], + }; +}; diff --git a/packages/modules/management/src/lib/components/role/role-create.component.ts b/packages/modules/management/src/lib/components/role/role-create.component.ts new file mode 100644 index 00000000..bbbd7628 --- /dev/null +++ b/packages/modules/management/src/lib/components/role/role-create.component.ts @@ -0,0 +1,40 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { combineLatest } from 'rxjs'; +import { tap } from 'rxjs/operators'; + +import { VCLFormFieldSchemaRoot } from '@vcl/ng-vcl'; + +import { RoleFacade } from '@console-core/state'; + +import { buildRoleSchema } from './jss-forms'; + +@Component({ + selector: 'app-module-management-role-create', + template: ` + +
+ +
+
+ `, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class RoleCreateComponent { + schema!: VCLFormFieldSchemaRoot; + create = this.roleFacade.create; + + readonly vm$ = combineLatest({ + role: this.roleFacade.selected$, + allDistinctAssignableByRoles: this.roleFacade.allDistinctAssignableByRoles$, + }).pipe( + tap(({ allDistinctAssignableByRoles }) => { + const assignableByRoles = allDistinctAssignableByRoles || []; + this.schema = buildRoleSchema({ assignableByRoles }); + }) + ); + + constructor(private readonly roleFacade: RoleFacade) {} +} diff --git a/packages/modules/management/src/lib/components/role/role-edit.component.ts b/packages/modules/management/src/lib/components/role/role-edit.component.ts new file mode 100644 index 00000000..ab09f01e --- /dev/null +++ b/packages/modules/management/src/lib/components/role/role-edit.component.ts @@ -0,0 +1,67 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { Router } from '@angular/router'; +import { combineLatest } from 'rxjs'; +import { map, tap } from 'rxjs/operators'; + +import { VCLFormFieldSchemaRoot } from '@vcl/ng-vcl'; + +import { ROUTER } from '@console-core/config'; +import { + RoleFacade, + RouterFacade, + filterEmptyAndNullishAndUndefined, +} from '@console-core/state'; + +import { buildRoleSchema } from './jss-forms'; + +@Component({ + selector: 'app-module-management-role-edit', + template: ` + +
+ +
+
+ `, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class RoleEditComponent { + schema!: VCLFormFieldSchemaRoot; + update = this.roleFacade.update; + + readonly vm$ = combineLatest({ + id: this.routerFacade.params$.pipe( + map(({ id }) => id), + tap((id) => { + this.roleFacade.setSelectedId(id); + }) + ), + role: this.roleFacade.selected$.pipe( + tap((role) => { + if (!role) { + this.roleFacade.setSelectedId(null); + this.router.navigate( + ROUTER.pages.main.children.management.children.accessControl.children.roles.children.index.getLink() + ); + } + }), + filterEmptyAndNullishAndUndefined() + ), + allDistinctAssignableByRoles: this.roleFacade.allDistinctAssignableByRoles$, + }).pipe( + tap(({ role, allDistinctAssignableByRoles }) => { + const assignableByRoles = allDistinctAssignableByRoles || []; + this.schema = buildRoleSchema({ role, assignableByRoles }); + }) + ); + + constructor( + private readonly router: Router, + private readonly routerFacade: RouterFacade, + private readonly roleFacade: RoleFacade + ) {} +} diff --git a/packages/modules/management/src/lib/components/role/role-index.component.ts b/packages/modules/management/src/lib/components/role/role-index.component.ts new file mode 100644 index 00000000..c7843a33 --- /dev/null +++ b/packages/modules/management/src/lib/components/role/role-index.component.ts @@ -0,0 +1,39 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { Router } from '@angular/router'; +import { combineLatest } from 'rxjs'; +import { tap } from 'rxjs/operators'; + +import { ROUTER } from '@console-core/config'; +import { + RoleFacade, + filterEmptyAndNullishAndUndefined, +} from '@console-core/state'; + +@Component({ + selector: 'app-module-management-role-index', + template: ` + +

Roles

+
+ `, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class RoleIndexComponent { + readonly vm$ = combineLatest({ + selectedRoleId: this.roleFacade.selectedId$.pipe( + filterEmptyAndNullishAndUndefined(), + tap((id) => { + this.router.navigate( + ROUTER.pages.main.children.management.children.accessControl.children.roles.children.view.getLink( + { id } + ) + ); + }) + ), + }); + + constructor( + private readonly router: Router, + private readonly roleFacade: RoleFacade + ) {} +} diff --git a/packages/modules/management/src/lib/components/role/role-view-details.component.ts b/packages/modules/management/src/lib/components/role/role-view-details.component.ts new file mode 100644 index 00000000..3b1dfb3d --- /dev/null +++ b/packages/modules/management/src/lib/components/role/role-view-details.component.ts @@ -0,0 +1,39 @@ +import { Component, Input } from '@angular/core'; + +import { IRole } from '@console-core/types'; + +@Component({ + selector: 'app-module-management-role-view-details', + template: ` +
+
Data
+
    +
  • +
    Name:
    +
    + {{ role.name }} +
    +
  • +
  • +
    Description:
    +
    + {{ role.description }} +
    +
  • + +
  • +
    Assignable by roles:
    +
    +
    +              {{ role.assignableByRoles | json }}
    +              
    +
    +
  • +
+
+ `, +}) +export class RoleViewDetailsComponent { + @Input({ required: true }) role!: IRole; +} diff --git a/packages/modules/management/src/lib/components/role/role-view.component.ts b/packages/modules/management/src/lib/components/role/role-view.component.ts new file mode 100644 index 00000000..f18a3001 --- /dev/null +++ b/packages/modules/management/src/lib/components/role/role-view.component.ts @@ -0,0 +1,50 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { Router } from '@angular/router'; +import { combineLatest } from 'rxjs'; +import { map, tap } from 'rxjs/operators'; + +import { ROUTER } from '@console-core/config'; +import { + RoleFacade, + RouterFacade, + filterEmptyAndNullishAndUndefined, +} from '@console-core/state'; + +@Component({ + selector: 'app-module-management-role-view', + template: ` + + + + `, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class RoleViewComponent { + readonly vm$ = combineLatest({ + id: this.routerFacade.params$.pipe( + map(({ id }) => id), + filterEmptyAndNullishAndUndefined(), + tap((id) => { + this.roleFacade.setSelectedId(id); + this.roleFacade.readOneById({ id }); + }) + ), + role: this.roleFacade.selected$.pipe( + tap((role) => { + if (!role) { + this.roleFacade.setSelectedId(null); + this.router.navigate( + ROUTER.pages.main.children.management.children.accessControl.children.roles.children.index.getLink() + ); + } + }), + filterEmptyAndNullishAndUndefined() + ), + }); + + constructor( + private readonly router: Router, + private readonly routerFacade: RouterFacade, + private readonly roleFacade: RoleFacade + ) {} +} diff --git a/packages/modules/management/src/lib/components/role/role.component.ts b/packages/modules/management/src/lib/components/role/role.component.ts deleted file mode 100644 index 25a67bbc..00000000 --- a/packages/modules/management/src/lib/components/role/role.component.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; -import { map } from 'rxjs/operators'; - -@Component({ - selector: 'app-module-management-access-control-role', - template: ` -
- -

Role: {{ id }}

-
- - -

Roles

-
-
- `, - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class RoleComponent { - id$ = this.route.params.pipe(map((params) => params['id'])); - - constructor(private readonly route: ActivatedRoute) {} -} diff --git a/packages/modules/management/src/lib/components/role/role.module.ts b/packages/modules/management/src/lib/components/role/role.module.ts index d07d996c..c5825a16 100644 --- a/packages/modules/management/src/lib/components/role/role.module.ts +++ b/packages/modules/management/src/lib/components/role/role.module.ts @@ -4,23 +4,69 @@ import { RouterModule, Routes } from '@angular/router'; import { ROUTER } from '@console-core/config'; import { ModulesUiModule } from '@console-modules/ui'; -import { RoleComponent } from './role.component'; +import { RoleCreateComponent } from './role-create.component'; +import { RoleEditComponent } from './role-edit.component'; +import { RoleIndexComponent } from './role-index.component'; +import { RoleViewDetailsComponent } from './role-view-details.component'; +import { RoleViewComponent } from './role-view.component'; +import { RoleTemplateComponent } from './template/role-template.component'; const routes: Routes = [ { - path: ROUTER.pages.main.children.management.children.accessControl.children - .roles.children.index.path, - component: RoleComponent, - }, - { - path: ROUTER.pages.main.children.management.children.accessControl.children - .roles.children.roles.path, - component: RoleComponent, + path: '', + component: RoleTemplateComponent, + children: [ + { + path: ROUTER.pages.main.children.management.children.accessControl + .children.roles.children.index.path, + component: RoleIndexComponent, + title: + ROUTER.pages.main.children.management.children.accessControl.children + .roles.children.index.title, + }, + { + path: ROUTER.pages.main.children.management.children.accessControl + .children.roles.children.view.path, + component: RoleViewComponent, + title: + ROUTER.pages.main.children.management.children.accessControl.children + .roles.children.view.title, + }, + { + path: ROUTER.pages.main.children.management.children.accessControl + .children.roles.children.create.path, + component: RoleCreateComponent, + title: + ROUTER.pages.main.children.management.children.accessControl.children + .roles.children.create.title, + }, + { + path: ROUTER.pages.main.children.management.children.accessControl + .children.roles.children.edit.path, + component: RoleEditComponent, + title: + ROUTER.pages.main.children.management.children.accessControl.children + .roles.children.edit.title, + }, + { + path: '**', + redirectTo: + ROUTER.pages.main.children.management.children.accessControl.children + .roles.children.index.path, + }, + ], }, ]; @NgModule({ - declarations: [RoleComponent], + declarations: [ + RoleIndexComponent, + RoleCreateComponent, + RoleEditComponent, + RoleViewComponent, + RoleViewDetailsComponent, + RoleTemplateComponent, + ], imports: [ModulesUiModule.forChild(), RouterModule.forChild(routes)], }) export class RoleModule {} diff --git a/packages/modules/management/src/lib/components/role/template/role-template.component.html b/packages/modules/management/src/lib/components/role/template/role-template.component.html new file mode 100644 index 00000000..5f51d8d2 --- /dev/null +++ b/packages/modules/management/src/lib/components/role/template/role-template.component.html @@ -0,0 +1,52 @@ + + +
+ +
+
+ + + + + + + + + + + + + + +
+ + +
+ +
+
diff --git a/packages/modules/management/src/lib/components/role/template/role-template.component.ts b/packages/modules/management/src/lib/components/role/template/role-template.component.ts new file mode 100644 index 00000000..415cf024 --- /dev/null +++ b/packages/modules/management/src/lib/components/role/template/role-template.component.ts @@ -0,0 +1,141 @@ +import { + ChangeDetectionStrategy, + Component, + OnDestroy, + OnInit, +} from '@angular/core'; +import { BehaviorSubject, combineLatest } from 'rxjs'; +import { + map, + tap, + distinctUntilChanged, + debounceTime, + skip, +} from 'rxjs/operators'; +import { SubSink } from 'subsink'; + +import { ROUTER } from '@console-core/config'; +import { + IIoRestorecommerceResourcebaseReadRequest, + IoRestorecommerceResourcebaseSortSortOrder, +} from '@console-core/graphql'; +import { RoleFacade, RouterFacade } from '@console-core/state'; +import { ICrudFeature, EUrlSegment, IRole } from '@console-core/types'; + +@Component({ + selector: 'app-module-management-role-template', + templateUrl: './role-template.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class RoleTemplateComponent implements OnInit, OnDestroy { + ROUTER = ROUTER; + featureRouter = + ROUTER.pages.main.children.management.children.accessControl.children.roles + .children; + + feature: Readonly = { + name: { + plural: 'Roles', + singular: 'Role', + }, + links: { + index: () => this.featureRouter.index.getLink(), + create: () => this.featureRouter.create.getLink(), + edit: (id: string | null) => + this.featureRouter.edit.getLink({ id: id ?? undefined }), + view: (id: string | null) => + this.featureRouter.view.getLink({ id: id ?? undefined }), + }, + }; + + queryVariables: IIoRestorecommerceResourcebaseReadRequest = { + sorts: [ + { + field: 'name', + order: IoRestorecommerceResourcebaseSortSortOrder.Ascending, + }, + ], + }; + + readonly triggerRead = new BehaviorSubject(null); + readonly triggerRead$ = this.triggerRead + .asObservable() + .pipe(tap(() => this.roleFacade.read(this.queryVariables))); + + readonly triggerSearch = new BehaviorSubject(''); + readonly triggerSearch$ = this.triggerSearch.asObservable().pipe( + skip(1), + debounceTime(300), + distinctUntilChanged(), + tap((value) => { + this.queryVariables = { + ...this.queryVariables, + search: { + caseSensitive: false, + search: value, + fields: ['name', 'geographical_name', 'role_code'], + }, + }; + this.roleFacade.read(this.queryVariables); + }) + ); + + readonly triggerSelectId = new BehaviorSubject(null); + readonly triggerSelectId$ = this.triggerSelectId + .asObservable() + .pipe(tap((id) => this.roleFacade.setSelectedId(id))); + + readonly triggerRemove = new BehaviorSubject(null); + readonly triggerRemove$ = this.triggerRemove.asObservable().pipe( + tap((id) => { + if (id === null) { + return; + } + this.roleFacade.remove({ id }); + }) + ); + + readonly urlSegment$ = this.routerFacade.url$.pipe( + map((url) => url.split('/').pop() as EUrlSegment), + distinctUntilChanged(), + tap((segment) => { + if ([EUrlSegment.Index, EUrlSegment.Create].includes(segment)) { + this.roleFacade.setSelectedId(null); + } + }), + debounceTime(10) + ); + + readonly vm$ = combineLatest({ + dataList: this.roleFacade.all$, + selectedRoleId: this.roleFacade.selectedId$, + selectedRole: this.roleFacade.selected$, + urlSegment: this.urlSegment$, + triggerRead: this.triggerRead$, + triggerSelectId: this.triggerSelectId$, + triggerRemove: this.triggerRemove$, + }); + + private readonly subscriptions = new SubSink(); + + constructor( + private readonly roleFacade: RoleFacade, + private readonly routerFacade: RouterFacade + ) {} + + ngOnInit(): void { + this.subscriptions.sink = this.triggerSearch$.subscribe(); + } + + ngOnDestroy(): void { + this.subscriptions.unsubscribe(); + } + + onSearch(value: string): void { + this.triggerSearch.next(value); + } + + trackByFn(_: number, item: IRole) { + return item.id; + } +} diff --git a/packages/modules/order/src/lib/components/order-create.component.ts b/packages/modules/order/src/lib/components/order-create.component.ts index 29eeae8f..415ca681 100644 --- a/packages/modules/order/src/lib/components/order-create.component.ts +++ b/packages/modules/order/src/lib/components/order-create.component.ts @@ -5,7 +5,7 @@ import { VCLFormFieldSchemaRoot } from '@vcl/ng-vcl'; import { OrderFacade } from '@console-core/state'; -import { buildOrderSchema } from './jss-forms'; +import { buildOrderSchema } from '../jss-forms'; @Component({ selector: 'app-module-order-create', diff --git a/packages/modules/order/src/lib/components/order-edit.component.ts b/packages/modules/order/src/lib/components/order-edit.component.ts index 76e2fa7b..5da2d85c 100644 --- a/packages/modules/order/src/lib/components/order-edit.component.ts +++ b/packages/modules/order/src/lib/components/order-edit.component.ts @@ -12,7 +12,7 @@ import { filterEmptyAndNullishAndUndefined, } from '@console-core/state'; -import { buildOrderSchema } from './jss-forms'; +import { buildOrderSchema } from '../jss-forms'; @Component({ selector: 'app-module-order-edit', diff --git a/packages/modules/order/src/lib/components/order-view.component.ts b/packages/modules/order/src/lib/components/order-view.component.ts index 38c2de19..d448722f 100644 --- a/packages/modules/order/src/lib/components/order-view.component.ts +++ b/packages/modules/order/src/lib/components/order-view.component.ts @@ -26,6 +26,7 @@ export class OrderViewComponent { filterEmptyAndNullishAndUndefined(), tap((id) => { this.orderFacade.setSelectedId(id); + this.orderFacade.readOneById({ id }); }) ), order: this.orderFacade.selected$.pipe( diff --git a/packages/modules/order/src/lib/components/jss-forms/index.ts b/packages/modules/order/src/lib/jss-forms/index.ts similarity index 100% rename from packages/modules/order/src/lib/components/jss-forms/index.ts rename to packages/modules/order/src/lib/jss-forms/index.ts diff --git a/packages/modules/order/src/lib/components/jss-forms/order.jss-form.ts b/packages/modules/order/src/lib/jss-forms/order.jss-form.ts similarity index 100% rename from packages/modules/order/src/lib/components/jss-forms/order.jss-form.ts rename to packages/modules/order/src/lib/jss-forms/order.jss-form.ts diff --git a/packages/modules/product/src/lib/components/product-create.component.ts b/packages/modules/product/src/lib/components/product-create.component.ts index e9966032..32328c83 100644 --- a/packages/modules/product/src/lib/components/product-create.component.ts +++ b/packages/modules/product/src/lib/components/product-create.component.ts @@ -5,7 +5,7 @@ import { VCLFormFieldSchemaRoot } from '@vcl/ng-vcl'; import { ProductFacade } from '@console-core/state'; -import { buildProductSchema } from './jss-forms'; +import { buildProductSchema } from '../jss-forms'; @Component({ selector: 'app-module-product-create', diff --git a/packages/modules/product/src/lib/components/product-edit.component.ts b/packages/modules/product/src/lib/components/product-edit.component.ts index 41993d95..deb995d8 100644 --- a/packages/modules/product/src/lib/components/product-edit.component.ts +++ b/packages/modules/product/src/lib/components/product-edit.component.ts @@ -12,7 +12,7 @@ import { filterEmptyAndNullishAndUndefined, } from '@console-core/state'; -import { buildProductSchema } from './jss-forms'; +import { buildProductSchema } from '../jss-forms'; @Component({ selector: 'app-module-product-edit', diff --git a/packages/modules/product/src/lib/components/product-view.component.ts b/packages/modules/product/src/lib/components/product-view.component.ts index dc328574..9f942517 100644 --- a/packages/modules/product/src/lib/components/product-view.component.ts +++ b/packages/modules/product/src/lib/components/product-view.component.ts @@ -26,6 +26,7 @@ export class ProductViewComponent { filterEmptyAndNullishAndUndefined(), tap((id) => { this.productFacade.setSelectedId(id); + this.productFacade.readOneById({ id }); }) ), product: this.productFacade.selected$.pipe( diff --git a/packages/modules/product/src/lib/components/jss-forms/index.ts b/packages/modules/product/src/lib/jss-forms/index.ts similarity index 100% rename from packages/modules/product/src/lib/components/jss-forms/index.ts rename to packages/modules/product/src/lib/jss-forms/index.ts diff --git a/packages/modules/product/src/lib/components/jss-forms/product.jss-form.ts b/packages/modules/product/src/lib/jss-forms/product.jss-form.ts similarity index 100% rename from packages/modules/product/src/lib/components/jss-forms/product.jss-form.ts rename to packages/modules/product/src/lib/jss-forms/product.jss-form.ts diff --git a/packages/modules/shared/src/lib/modules-shared.module.ts b/packages/modules/shared/src/lib/modules-shared.module.ts index 42dda59f..f60f820b 100644 --- a/packages/modules/shared/src/lib/modules-shared.module.ts +++ b/packages/modules/shared/src/lib/modules-shared.module.ts @@ -1,8 +1,4 @@ import { NgModule } from '@angular/core'; -import { ModulesUiModule } from '@console-modules/ui'; - -@NgModule({ - imports: [ModulesUiModule.forChild()], -}) +@NgModule({}) export class ModulesSharedModule {} diff --git a/packages/modules/ui/src/lib/components/molecules/product-details/product-details.component.html b/packages/modules/ui/src/lib/components/molecules/product-details/product-details.component.html index 155bed60..eafd80a5 100644 --- a/packages/modules/ui/src/lib/components/molecules/product-details/product-details.component.html +++ b/packages/modules/ui/src/lib/components/molecules/product-details/product-details.component.html @@ -23,7 +23,7 @@ {{ taxId }} diff --git a/packages/modules/ui/src/lib/components/organisms/crud/crud-main.component.html b/packages/modules/ui/src/lib/components/organisms/crud/crud-main.component.html index 8f13cca2..97d10b06 100644 --- a/packages/modules/ui/src/lib/components/organisms/crud/crud-main.component.html +++ b/packages/modules/ui/src/lib/components/organisms/crud/crud-main.component.html @@ -128,7 +128,10 @@ *ngIf="!vm.isLg && id" class="row" > -
+
Meta
diff --git a/packages/modules/ui/src/lib/components/organisms/crud/crud-main.component.ts b/packages/modules/ui/src/lib/components/organisms/crud/crud-main.component.ts index f926f277..c3432a13 100644 --- a/packages/modules/ui/src/lib/components/organisms/crud/crud-main.component.ts +++ b/packages/modules/ui/src/lib/components/organisms/crud/crud-main.component.ts @@ -30,6 +30,7 @@ export class RcCrudMainComponent implements OnInit, OnDestroy { @Input() triggerCreateInvoice?: BehaviorSubject; @Input() triggerCreateFulfillment?: BehaviorSubject; @Input() title = ''; + @Input() isMeta = true; @Input() isRead = true; @Input() isCreate = true; @Input() isEdit = true; diff --git a/packages/modules/ui/src/lib/components/organisms/data-list/data-list-item.component.ts b/packages/modules/ui/src/lib/components/organisms/data-list/data-list-item.component.ts index e8307424..16db4c85 100644 --- a/packages/modules/ui/src/lib/components/organisms/data-list/data-list-item.component.ts +++ b/packages/modules/ui/src/lib/components/organisms/data-list/data-list-item.component.ts @@ -44,7 +44,7 @@ export class RcDataListSublabelComponent { selector: 'rc-data-list-key-value', template: ` {{ key }}: - [{{ value }}] + {{ value }} `, changeDetection: ChangeDetectionStrategy.OnPush, }) @@ -55,3 +55,19 @@ export class RcDataListKeyValueComponent { @Input({ required: true }) key!: string; @Input({ required: true }) value!: string; } + +@Component({ + selector: 'rc-data-list-key-value-with-brackets', + template: ` + {{ key }}: + [{{ value }}] + `, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class RcDataListKeyValueWithBracketsComponent { + @HostBinding('class.data-list-key-value') + _hostClasses = true; + + @Input({ required: true }) key!: string; + @Input({ required: true }) value!: string; +} diff --git a/packages/modules/ui/src/lib/components/organisms/drawer/drawer-navigation.component.html b/packages/modules/ui/src/lib/components/organisms/drawer/drawer-navigation.component.html index 3027af76..bd3cb594 100644 --- a/packages/modules/ui/src/lib/components/organisms/drawer/drawer-navigation.component.html +++ b/packages/modules/ui/src/lib/components/organisms/drawer/drawer-navigation.component.html @@ -3,6 +3,7 @@ role="menu" (mouseenter)="onSidebarMouseEnter()" (mouseleave)="onSidebarMouseLeave()" + style="margin-bottom: 90px" >
diff --git a/packages/modules/ui/src/lib/components/organisms/fulfillment/fulfillment-view.component.html b/packages/modules/ui/src/lib/components/organisms/fulfillment/fulfillment-view.component.html deleted file mode 100644 index 5270ce12..00000000 --- a/packages/modules/ui/src/lib/components/organisms/fulfillment/fulfillment-view.component.html +++ /dev/null @@ -1,4 +0,0 @@ - -
-  {{ fulfillment | json }}
-
diff --git a/packages/modules/ui/src/lib/components/organisms/index.ts b/packages/modules/ui/src/lib/components/organisms/index.ts index 25246242..a95aa005 100644 --- a/packages/modules/ui/src/lib/components/organisms/index.ts +++ b/packages/modules/ui/src/lib/components/organisms/index.ts @@ -26,10 +26,7 @@ export * from './header/header-navigation-item.component'; export * from './header/header-navigation.component'; export * from './header/header-toolbar.component'; export * from './header/header.component'; -export * from './invoice/invoice-view.component'; export * from './flex-grid/flex-grid.directive'; -export * from './fulfillment/fulfillment-view.component'; -export * from './management/country/country-view.component'; export * from './meta/meta.component'; export * from './order/order-view.component'; export * from './page-header/page-header.component'; diff --git a/packages/modules/ui/src/lib/components/organisms/invoice/invoice-view.component.html b/packages/modules/ui/src/lib/components/organisms/invoice/invoice-view.component.html deleted file mode 100644 index adb38515..00000000 --- a/packages/modules/ui/src/lib/components/organisms/invoice/invoice-view.component.html +++ /dev/null @@ -1,4 +0,0 @@ - -
-  {{ invoice | json }}
-
diff --git a/packages/modules/ui/src/lib/components/organisms/product/product-view.component.html b/packages/modules/ui/src/lib/components/organisms/product/product-view.component.html deleted file mode 100644 index 7e67e428..00000000 --- a/packages/modules/ui/src/lib/components/organisms/product/product-view.component.html +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/packages/modules/ui/src/lib/components/organisms/product/product-view.component.ts b/packages/modules/ui/src/lib/components/organisms/product/product-view.component.ts index 4f5b03f9..f7695d76 100644 --- a/packages/modules/ui/src/lib/components/organisms/product/product-view.component.ts +++ b/packages/modules/ui/src/lib/components/organisms/product/product-view.component.ts @@ -4,7 +4,12 @@ import { IoRestorecommerceProductProduct } from '@console-core/graphql'; @Component({ selector: 'rc-product-view', - templateUrl: './product-view.component.html', + template: ` + + + `, changeDetection: ChangeDetectionStrategy.OnPush, }) export class RcProductViewComponent { diff --git a/packages/modules/ui/src/lib/modules-ui.module.ts b/packages/modules/ui/src/lib/modules-ui.module.ts index d5d75070..2500fcce 100644 --- a/packages/modules/ui/src/lib/modules-ui.module.ts +++ b/packages/modules/ui/src/lib/modules-ui.module.ts @@ -57,6 +57,7 @@ import { RcDataListOrderComponent, RcDataListProductComponent, RcDataListKeyValueComponent, + RcDataListKeyValueWithBracketsComponent, RcDataListComponent, RcDrawerNavigationItemComponent, RcDrawerNavigationComponent, @@ -81,11 +82,8 @@ import { RcCrudMainComponent, RcCrudCreateComponent, RcCrudEditComponent, - RcCountryViewComponent, RcOrderViewComponent, RcProductViewComponent, - RcFulfillmentViewComponent, - RcInvoiceViewComponent, } from './components/organisms'; import { RcPageConfirmPasswordComponent, @@ -178,6 +176,7 @@ const organisms = [ RcDataListOrderComponent, RcDataListProductComponent, RcDataListKeyValueComponent, + RcDataListKeyValueWithBracketsComponent, RcMetaComponent, RcSearchbarComponent, RcToolbarComponent, @@ -196,11 +195,8 @@ const organisms = [ RcCrudMainComponent, RcCrudCreateComponent, RcCrudEditComponent, - RcCountryViewComponent, RcOrderViewComponent, RcProductViewComponent, - RcFulfillmentViewComponent, - RcInvoiceViewComponent, ]; const pages = [ diff --git a/packages/modules/ui/src/lib/styles/forms.scss b/packages/modules/ui/src/lib/styles/forms.scss index 13fd352e..caf2e07b 100644 --- a/packages/modules/ui/src/lib/styles/forms.scss +++ b/packages/modules/ui/src/lib/styles/forms.scss @@ -1,3 +1,5 @@ +@use '@vcl/vcl/theme' as *; + vcl-jss-form { .loose-button-group { display: flex; @@ -16,4 +18,17 @@ vcl-jss-form { } } } + + vcl-jss-form-array { + .fieldset { + border: 1px solid $input-border-color; + border-radius: 0; + margin-inline-start: -1px; + margin-inline-end: 0; + + legend { + font-weight: normal; + } + } + } }