Sales import Working

This commit is contained in:
tanshu committed 2026-09-15 04:10:38 +00:00
1 parent 7be26ad9da
commit 7656c7e877
11 files changed
+190 -77

No files matched your search

@@ -16,7 +16,7 @@
<div class="two-col">
<div class="col">
<h3>Permissions</h3>
@for (p of item().permissions; track p; let i = $index) {
@for (p of item().permissions; track p.id; let i = $index) {
<div class="row-container">
<mat-checkbox
[formField]="form.permissions[i].permission"
@@ -30,7 +30,7 @@
<div class="col">
<h3>Includes Roles</h3>
@for (r of item().includedRoles; track r; let i = $index) {
@for (r of item().includedRoles; track r.id; let i = $index) {
<div class="row-container">
<mat-checkbox [formField]="form.includedRoles[i].role" class="flex-auto">{{ r.name }}</mat-checkbox>
</div>
@@ -1,4 +1,13 @@
import { Component, inject, afterNextRender, effect, input, computed, linkedSignal } from '@angular/core';
import {
Component,
computed,
effect,
inject,
input,
linkedSignal,
untracked,
afterNextRender,
} from '@angular/core';
import { form as createForm, FormField, FormRoot } from '@angular/forms/signals';
import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox';
@@ -12,14 +21,31 @@ import { Router } from '@angular/router';
import { ConfirmDialogComponent } from '../../shared/confirm-dialog/confirm-dialog.component';
import { ErrorStateComponent } from '../../shared/error-state/error-state.component';
import { SkeletonLoaderComponent } from '../../shared/skeleton-loader/skeleton-loader.component';
import { Role } from '../role';
import { Permission } from '../permission';
import { Role, RoleItem } from '../role';
import { RoleService } from '../role.service';
export interface RoleDetailFormData {
name: string;
permissions: {
id: string;
name: string;
permission: boolean;
direct: boolean;
disabled: boolean;
}[];
includedRoles: {
id: string;
name: string;
role: boolean;
permissionIds: string[];
}[];
}
@Component({
selector: 'app-role-detail',
templateUrl: './role-detail.component.html',
styleUrls: ['./role-detail.component.css'],
imports: [
FormField,
FormRoot,
@@ -44,63 +70,93 @@ export class RoleDetailComponent {
model = linkedSignal({
source: this.item,
computation: (itemVal) => ({
name: itemVal.name ?? '',
permissions: itemVal.permissions.map((x) => ({
permission: x.enabled,
disabled: false,
})),
includedRoles: itemVal.includedRoles.map((x) => ({
role: x.enabled,
})),
}),
computation: (itemVal): RoleDetailFormData => {
const implied = new Set<string>();
(itemVal.includedRoles || []).forEach((r) => {
if (r.enabled) {
(r.permissionIds || []).forEach((pid) => implied.add(pid));
}
});
return {
name: itemVal.name ?? '',
permissions: (itemVal.permissions || []).map((p) => {
const isImplied = implied.has(p.id as string);
const isDirect = p.enabled === true;
return {
id: p.id ?? '',
name: p.name,
permission: isImplied || isDirect,
direct: isDirect,
disabled: isImplied,
};
}),
includedRoles: (itemVal.includedRoles || []).map((r) => ({
id: r.id ?? '',
name: r.name,
role: r.enabled === true,
permissionIds: r.permissionIds || [],
})),
};
},
});
form = createForm(this.model);
private enabledIncludedRoleIds = computed(() => {
return this.model()
.includedRoles.filter((r) => r.role)
.map((r) => r.id)
.sort()
.join(',');
});
constructor() {
afterNextRender(() => {
this.form.name().focusBoundControl();
});
effect(() => {
// Track changes to includedRoles explicitly
this.model().includedRoles.map((x) => x.role);
this.applyIncludedRolePermissionLocks();
// Recompute permission locks whenever the set of enabled included roles changes
this.enabledIncludedRoleIds();
untracked(() => {
this.applyIncludedRolePermissionLocks();
});
});
}
private applyIncludedRolePermissionLocks(): void {
this.model.update((currentModel) => {
const implied = new Set<string>();
const currentItem = this.item();
currentItem.includedRoles.forEach((roleOpt, idx) => {
const checked = currentModel.includedRoles[idx]?.role;
if (checked) {
(roleOpt.permissionIds || []).forEach((pid) => implied.add(pid));
currentModel.includedRoles.forEach((role) => {
if (role.role) {
(role.permissionIds || []).forEach((pid) => implied.add(pid));
}
});
const newPerms = [...currentModel.permissions];
let changed = false;
const newPerms = currentModel.permissions.map((p) => {
// If not disabled, direct state tracks the user's checkbox value
const currentDirect = p.disabled ? p.direct : p.permission;
const isImplied = implied.has(p.id);
currentItem.permissions.forEach((perm, idx) => {
const isImplied = implied.has(perm.id as string);
const isDirect = perm.enabled === true;
const newPermission = isImplied ? true : currentDirect;
const newDisabled = isImplied;
if (!newPerms[idx]) return;
if (isImplied) {
if (!newPerms[idx].permission || !newPerms[idx].disabled) {
newPerms[idx] = { permission: true, disabled: true };
changed = true;
}
} else {
if (newPerms[idx].permission !== isDirect || newPerms[idx].disabled) {
newPerms[idx] = { permission: isDirect, disabled: false };
changed = true;
}
if (
p.direct !== currentDirect ||
p.permission !== newPermission ||
p.disabled !== newDisabled
) {
changed = true;
return {
...p,
direct: currentDirect,
permission: newPermission,
disabled: newDisabled,
};
}
return p;
});
if (changed) {
@@ -149,21 +205,26 @@ export class RoleDetailComponent {
getItem(): Role {
const formModel = this.model();
const currentItem = this.item();
currentItem.name = formModel.name ?? '';
const permArray = formModel.permissions;
currentItem.permissions.forEach((p, index) => {
if (permArray[index]?.disabled) {
return;
}
p.enabled = permArray[index]?.permission ?? false;
return new Role({
...this.item(),
name: formModel.name.trim(),
permissions: formModel.permissions.map(
(p) =>
new Permission({
id: p.id,
name: p.name,
enabled: p.disabled ? p.direct : p.permission,
}),
),
includedRoles: formModel.includedRoles.map(
(r) =>
new RoleItem({
id: r.id,
name: r.name,
enabled: r.role,
permissionIds: r.permissionIds,
}),
),
});
const includeArray = formModel.includedRoles;
currentItem.includedRoles.forEach((r, index) => {
r.enabled = includeArray[index]?.role ?? false;
});
return currentItem;
}
}
@@ -164,6 +164,6 @@ export class SalesImportComponent {
}
trackLine(_index: number, line: SaleLine): string {
return `${line.skuId ?? line.productName}-${line.rate}-${line.isHappyHour}`;
return `${line.skuId ?? line.name}-${line.rate}-${line.isHappyHour}`;
}
}