third party form

mazdak/UI-transcation-form
Mazdak Gibran 1 month ago
parent b12008db80
commit 139f5e7384

@ -1,67 +1,295 @@
<div class="page-content">
<div id="layout-wrapper">
<div class="inner-pg-sp">
<div class="container-fluid">
<!-- User Selection Card -->
<div class="card shadow-sm mb-4">
<div class="card-body">
<form [formGroup]="permission" class="row align-items-center">
<label class="col-md-2 col-form-label fw-semibold">
{{ "userCode" | translate }}
</label>
<div class="col-md-6">
<ng-select class="form-select" formControlName="userCode" [items]="users" bindLabel="userName"
bindValue="userId" placeholder="{{ 'choose' | translate }}" (change)="onUserChange()"
[searchable]="true" [clearable]="false" [dropdownPosition]="'auto'" [virtualScroll]="true"
[bufferAmount]="20" appendTo="body">
<!-- Custom template for dropdown options -->
<ng-template ng-option-tmp let-item="item">
<div class="d-flex flex-column">
<span class="fw-medium">{{ item.userName }}</span>
<small class="text-muted">{{ item.userId }}</small>
</div>
</ng-template>
<!-- Optional: Custom template for selected item -->
<ng-template ng-label-tmp let-item="item">
<span>{{ item.userName }} ({{ item.userId }})</span>
</ng-template>
</ng-select>
</div>
</form>
</div>
<div class="row">
<div class="col-12">
<div class="d-sm-flex align-items-center justify-content-between navbar-header p-0"></div>
</div>
</div>
<div class="container-fluid">
<div class="col-xl-12 mt-4">
<div class="card shadow-sm" *ngIf="showPermissions">
<!-- CARD 1: User Selection + Permissions Table -->
<div class="card border mb-4">
<div class="card-body">
<h4 class="card-title mb-3">{{ "menu" | translate }}</h4>
<div class="table-responsive scrollable-table">
<table class="table table-hover table-bordered table-sm permission-table">
<thead class="table-light">
<tr>
<th style="width: 50px">#</th>
<th>{{ "type" | translate }}</th>
<th style="width: 100px;" class="text-center">{{ "allow" | translate }}</th>
</tr>
</thead>
<tbody>
<div class="table-section">
<div class="row">
<div class="col-lg-12">
<div class="card-body mt-2 p-0">
<div class="card mb-0 mt-2">
<div class="card-header font-edit-13-child mb-3">
{{ "userPermission" | translate }}
</div>
<div class="card-body">
<!-- User Selection -->
<div class="card shadow-sm mb-4">
<div class="card-body">
<form [formGroup]="permission" class="row align-items-center">
<label class="col-md-2 col-form-label fw-semibold">
{{ "userCode" | translate }}
</label>
<div class="col-md-6">
<ng-select class="form-select" formControlName="userCode" [items]="users"
bindLabel="userName" bindValue="userId"
placeholder="{{ 'choose' | translate }}" (change)="onUserChange()"
[searchable]="true" [clearable]="false" [dropdownPosition]="'auto'"
[virtualScroll]="true" [bufferAmount]="20" appendTo="body">
<ng-template ng-option-tmp let-item="item">
<div class="d-flex flex-column">
<span class="fw-medium">{{ item.userName }}</span>
<small class="text-muted">{{ item.userId }}</small>
</div>
</ng-template>
<ng-template ng-label-tmp let-item="item">
<span>{{ item.userName }} ({{ item.userId }})</span>
</ng-template>
</ng-select>
</div>
</form>
</div>
</div>
<!-- Permissions Table -->
<div class="card shadow-sm" *ngIf="showPermissions">
<div class="card-body">
<h4 class="card-title mb-3">{{ "menu" | translate }}</h4>
<div class="table-responsive scrollable-table">
<table class="table table-hover table-bordered table-sm permission-table">
<thead class="table-light">
<tr>
<th style="width: 50px">#</th>
<th>{{ "type" | translate }}</th>
<th style="width: 100px;" class="text-center">{{ "allow" | translate }}</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of menuItems; let i = index">
<td class="text-muted">{{ i + 1 }}</td>
<td>{{ item.endpoint | translate }}</td>
<td class="text-center"><input type="checkbox"
[(ngModel)]="item.checked" /></td>
<td class="text-muted">{{ i + 1 }}</td>
<td>{{ item.endpoint | translate }}</td>
<td class="text-center">
<input type="checkbox" [(ngModel)]="item.checked" />
</td>
</tr>
</tbody>
</table>
</tbody>
</table>
</div>
<div class="text-end mt-3">
<button class="btn btn-primary btn-sm px-4" (click)="savePermissions()">
{{ "save" | translate }}
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- CARD 2: Create Third Party User Form -->
<div class="card border">
<div class="card-body">
<div class="table-section">
<div class="row">
<div class="col-lg-12">
<div class="card-body mt-2 p-0">
<div class="card mb-0 mt-2">
<div class="card-header font-edit-13-child mb-3">
{{ "createThirdPartyUser" | translate }}
</div>
<div class="card-body">
<div class="text-end mt-3">
<button class="btn btn-primary btn-sm px-4" (click)="savePermissions()">
{{ "save" | translate }}
</button>
<form [formGroup]="thirdPartyForm" autocomplete="off">
<div class="row g-3 mb-3">
<!-- SUS_USERCODE -->
<div class="col-md-6">
<div class="d-flex align-items-center gap-2">
<label for="SUS_USERCODE" class="text-nowrap">
{{ "SUS_USERCODE" | translate }}<span class="mandatory">*</span>
</label>
<div class="position-relative w-100">
<input type="text" id="SUS_USERCODE" class="form-control"
formControlName="SUS_USERCODE"
placeholder="{{ 'SUS_USERCODE' | translate }}"
/>
<div class="text-danger"
*ngIf="thirdPartyForm.get('SUS_USERCODE')?.touched && thirdPartyForm.get('SUS_USERCODE')?.invalid">
<div *ngIf="thirdPartyForm.get('SUS_USERCODE')?.errors?.['required']">
{{ "fieldRequired" | translate }}
</div>
</div>
</div>
</div>
</div>
<!-- SUS_EMPCODE -->
<div class="col-md-6">
<div class="d-flex align-items-center gap-2">
<label for="SUS_EMPCODE" class="text-nowrap">
{{ "SUS_EMPCODE" | translate }}<span class="mandatory">*</span>
</label>
<div class="position-relative w-100">
<input type="text" id="SUS_EMPCODE" class="form-control"
formControlName="SUS_EMPCODE"
placeholder="{{ 'SUS_EMPCODE' | translate }}"
appNoWhitespaces />
<div class="text-danger"
*ngIf="thirdPartyForm.get('SUS_EMPCODE')?.touched && thirdPartyForm.get('SUS_EMPCODE')?.invalid">
<div *ngIf="thirdPartyForm.get('SUS_EMPCODE')?.errors?.['required']">
{{ "fieldRequired" | translate }}
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row g-3 mb-3">
<!-- SUS_NAME -->
<div class="col-md-6">
<div class="d-flex align-items-center gap-2">
<label for="SUS_NAME" class="text-nowrap">
{{ "SUS_NAME" | translate }}<span class="mandatory">*</span>
</label>
<div class="position-relative w-100">
<input type="text" id="SUS_NAME" class="form-control"
formControlName="SUS_NAME"
placeholder="{{ 'SUS_NAME' | translate }}"
appNoWhitespaces />
<div class="text-danger"
*ngIf="thirdPartyForm.get('SUS_NAME')?.touched && thirdPartyForm.get('SUS_NAME')?.invalid">
<div *ngIf="thirdPartyForm.get('SUS_NAME')?.errors?.['required']">
{{ "fieldRequired" | translate }}
</div>
</div>
</div>
</div>
</div>
<!-- SUS_PASSWORD -->
<div class="col-md-6">
<div class="d-flex align-items-start gap-2">
<label for="SUS_PASSWORD" class="text-nowrap">
{{ "SUS_PASSWORD" | translate }}<span class="mandatory">*</span>
</label>
<div class="w-100">
<div class="password-wrapper">
<input type="password" id="SUS_PASSWORD" class="form-control" formControlName="SUS_PASSWORD"
placeholder="{{ 'SUS_PASSWORD' | translate }}" appNoWhitespaces />
</div>
<div class="text-danger"
*ngIf="thirdPartyForm.get('SUS_PASSWORD')?.touched && thirdPartyForm.get('SUS_PASSWORD')?.invalid">
<div *ngIf="thirdPartyForm.get('SUS_PASSWORD')?.errors?.['required']">
{{ "fieldRequired" | translate }}
</div>
<div *ngIf="thirdPartyForm.get('SUS_PASSWORD')?.errors?.['pattern']">
{{ "passwordPattern" | translate }}
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row g-3 mb-3">
<!-- SUS_EMAIL -->
<div class="col-md-6">
<div class="d-flex align-items-center gap-2">
<label for="SUS_EMAIL" class="text-nowrap">
{{ "SUS_EMAIL" | translate }}<span class="mandatory">*</span>
</label>
<div class="position-relative w-100">
<input type="email" id="SUS_EMAIL" class="form-control"
formControlName="SUS_EMAIL"
placeholder="{{ 'SUS_EMAIL' | translate }}"
appNoWhitespaces />
<div class="text-danger"
*ngIf="thirdPartyForm.get('SUS_EMAIL')?.touched && thirdPartyForm.get('SUS_EMAIL')?.invalid">
<div *ngIf="thirdPartyForm.get('SUS_EMAIL')?.errors?.['required']">
{{ "fieldRequired" | translate }}
</div>
<div *ngIf="thirdPartyForm.get('SUS_EMAIL')?.errors?.['email']">
{{ "invalidEmail" | translate }}
</div>
</div>
</div>
</div>
</div>
<!-- SUS_USERCELLNO -->
<div class="col-md-6">
<div class="d-flex align-items-center gap-2">
<label for="SUS_USERCELLNO" class="text-nowrap">
{{ "SUS_USERCELLNO" | translate }}<span class="mandatory">*</span>
</label>
<div class="position-relative w-100">
<input type="tel" id="SUS_USERCELLNO" class="form-control"
formControlName="SUS_USERCELLNO"
placeholder="{{ 'SUS_USERCELLNO' | translate }}"
appNoWhitespaces />
<div class="text-danger"
*ngIf="thirdPartyForm.get('SUS_USERCELLNO')?.touched && thirdPartyForm.get('SUS_USERCELLNO')?.invalid">
<div *ngIf="thirdPartyForm.get('SUS_USERCELLNO')?.errors?.['required']">
{{ "fieldRequired" | translate }}
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row g-3 mb-3">
<!-- SUS_ACTIVE -->
<div class="col-md-6">
<div class="d-flex align-items-center gap-2">
<input type="checkbox" id="SUS_ACTIVE" class="form-check-input"
formControlName="SUS_ACTIVE" />
<label for="SUS_ACTIVE" class="form-check-label">
{{ "SUS_ACTIVE" | translate }}
</label>
</div>
</div>
<!-- SUS_THIRDPARTY -->
<div class="col-md-6">
<div class="d-flex align-items-center gap-2">
<input type="checkbox" id="SUS_THIRDPARTY" class="form-check-input"
formControlName="SUS_THIRDPARTY" />
<label for="SUS_THIRDPARTY" class="form-check-label">
{{ "SUS_THIRDPARTY" | translate }}
</label>
</div>
</div>
</div>
<div class="text-end mt-3">
<button class="btn btn-primary btn-sm px-4" (click)="submitForm()">
{{ "save" | translate }}
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

@ -1,5 +1,5 @@
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms';
import { FormBuilder, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
import { PermissionNode } from '../utils/app.interfaces';
import { CredentialService } from '../services/credential.service';
import { I18NService } from '../services/i18n.service';
@ -15,12 +15,13 @@ import { URIService } from '../app.uri';
@Component({
selector: 'app-menu',
imports: [TranslateModule, ReactiveFormsModule, CommonModule, TranslateModule, NgSelectModule, FormsModule],
imports: [TranslateModule, ReactiveFormsModule, ReactiveFormsModule, CommonModule, TranslateModule, NgSelectModule, FormsModule],
templateUrl: './menu.component.html',
styleUrl: './menu.component.scss'
})
export class MenuComponent {
users: any[] = [];
thirdPartyForm!: FormGroup;
permission: FormGroup;
showPermissions = false;
permissions: PermissionNode[] = [];
@ -38,13 +39,78 @@ export class MenuComponent {
this.defaultPermissions().subscribe((data: PermissionNode[]) => {
this.permissions = data;
});
}
ngOnInit() {
this.getAllUsers();
this.loadTransactionEndpoints();
this.createThirdPartyUserForm();
}
createThirdPartyUserForm(): void{
this.thirdPartyForm = this.fb.group({
SUS_USERCODE: ["", [Validators.required]],
SUS_EMPCODE: ["", [Validators.required]],
SUS_NAME: ["", [Validators.required]],
SUS_PASSWORD: ['', [
Validators.required,
Validators.minLength(8),
Validators.maxLength(20),
Validators.pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]+$/)
]],
SUS_EMAIL: ["", [Validators.required, Validators.email]],
SUS_USERCELLNO: ["", [Validators.required]],
POR_ORGACODE: [this.credentialService.getPorOrgacode()],
SUS_ACTIVE: [true],
SUS_THIRDPARTY: [true],
})
}
submitForm() {
if (this.thirdPartyForm.invalid) {
this.thirdPartyForm.markAllAsTouched();
return;
}
const f = this.thirdPartyForm.value;
const payload = {
formId: "SH_SM_US_USER",
postProcessFormId: "SH_SM_US_USER",
workFlowId: null,
operation: "nonwizard",
porOrgacode: this.credentialService.getPorOrgacode(),
usercode: this.credentialService.getUserId(),
uniqueConstraints: [["SUS_USERCODE", "POR_ORGACODE"]],
formCounters: [],
susUsercode: f.SUS_USERCODE,
susName: f.SUS_NAME,
susEmpcode: f.SUS_EMPCODE,
susPassword: f.SUS_PASSWORD,
susEmail: f.SUS_EMAIL,
susUsercellno: f.SUS_USERCELLNO,
susActive: f.SUS_ACTIVE,
susThirdparty: f.SUS_THIRDPARTY,
}
console.log("payload",payload )
this.httpService.requestPOST(URIKey.CREATE_THIRD_PARTY_USER, payload)
.subscribe({
next: (response: any) => {
if (!(response instanceof HttpErrorResponse)) {
this.i18nService.success(SuccessMessages.SAVED_SUCCESSFULLY, []);
this.createThirdPartyUserForm();
}
}
// handle error codes e.g. ERR_TRX_0006
// Once backend is ready, check response for errorCode field
// e.g. if (response?.errorCode === 'ERR_TRX_0006') { show error }
});
}
loadTransactionEndpoints() {
this.httpService.requestGET<string[]>(URIKey.TRANSACTION_ENDPOINTS).subscribe((response) => {
if (!(response instanceof HttpErrorResponse)) {
@ -127,7 +193,9 @@ savePermissions() {
next: (response: any) => {
if (!(response instanceof HttpErrorResponse)) {
this.i18nService.success(SuccessMessages.SAVED_SUCCESSFULLY, []);
this.onUserChange(); // this triggers one GET — expected
this.permission.reset();
this.showPermissions = false;
this.menuItems.forEach(item => item.checked = false);
}
},
complete: () => {

@ -73,7 +73,7 @@
</div>
<div class="row g-3 mb-3">
<div class="col-md-6">
<div class="d-flex align-items-center gap-2">
<div class="d-flex align-items-start gap-2">
<label for="confirmPassword" class="text-nowrap">
{{ 'confirmPassword' | translate }}<span
class="mandatory">*</span>

@ -189,11 +189,12 @@
<!-- Password -->
<!-- Password field (only show in create mode) -->
<div class="col-md-6" *ngIf="mode === 'create'">
<div class="d-flex align-items-center gap-2">
<div class="d-flex align-items-start gap-2">
<label for="defaultPassword" class="text-nowrap">
{{ "password" | translate }}<span class="mandatory">*</span>
</label>
<div class="password-wrapper position-relative w-100">
<div class="w-100">
<div class="password-wrapper">
<input
id="defaultPassword"
[type]="passwordType"
@ -209,6 +210,7 @@
[showPassword]="true"
(onEyeClick)="toggleSetupPass()"
></app-password-hide-show>
</div>
<div
class="text-danger"
*ngIf="userForm.get('defaultPassword')?.touched && userForm.get('defaultPassword')?.invalid"

@ -22,7 +22,8 @@ export enum URIKey {
GL_TO_ACCOUNT = "GL_TO_ACCOUNT",
TRANSACTION_PERMISSIONS_ASSIGN = "TRANSACTION_PERMISSIONS_ASSIGN",
TRANSACTION_PERMISSIONS = "TRANSACTION_PERMISSIONS",
TRANSACTION_ENDPOINTS = "TRANSACTION_ENDPOINTS"
TRANSACTION_ENDPOINTS = "TRANSACTION_ENDPOINTS",
CREATE_THIRD_PARTY_USER = "CREATE_THIRD_PARTY_USER"
}

@ -121,6 +121,11 @@
"Id": "ENTITY_TRANSACTION_ENDPOINTS",
"URI": "/transaction-permissions/endpoints",
"UUID": "TRANSACTION_ENDPOINTS"
},
{
"Id": "ENTITY_CREATE_THIRD_PARTY_USER",
"URI": "/createThirdPartyUser",
"UUID": "CREATE_THIRD_PARTY_USER"
}
]
}

@ -339,5 +339,15 @@
"seventeen": "١٧",
"eighteen": "١٨",
"nineteen": "١٩",
"twenty": "٢٠"
"twenty": "٢٠",
"SUS_USERCODE": "رمز المستخدم",
"SUS_EMPCODE": "رمز الموظف",
"SUS_NAME": "الاسم الكامل",
"SUS_PASSWORD": "كلمة المرور",
"SUS_EMAIL": "البريد الإلكتروني",
"SUS_USERCELLNO": "رقم الجوال",
"SUS_ACTIVE": "الحساب نشط",
"SUS_THIRDPARTY": "طرف ثالث",
"createThirdPartyUser": "إنشاء مستخدم طرف ثالث",
"userPermission": "صلاحية المستخدم"
}

@ -321,7 +321,7 @@
"GL_TO_GL": "General Ledger to General Ledger",
"ACCOUNT_TO_GL": "Account to General Ledger",
"GL_TO_ACCOUNT": "General Ledger to Account",
"one": "1",
"one": "1",
"two": "2",
"three": "3",
"four": "4",
@ -340,5 +340,15 @@
"seventeen": "17",
"eighteen": "18",
"nineteen": "19",
"twenty": "20"
}
"twenty": "20",
"SUS_USERCODE": "User Code",
"SUS_EMPCODE": "Employee Code",
"SUS_NAME": "Full Name",
"SUS_PASSWORD": "Password",
"SUS_EMAIL": "Email",
"SUS_USERCELLNO": "Cell Number",
"SUS_ACTIVE": "Account Active",
"SUS_THIRDPARTY": "Third Party",
"createThirdPartyUser": "Create Third Party User",
"userPermission": "User Permission"
}
Loading…
Cancel
Save