Leaving Date
@@ -76,7 +76,7 @@
-
+
@if (!!item().id) {
}
diff --git a/overlord/src/app/employee/employee-detail/employee-detail.component.ts b/overlord/src/app/employee/employee-detail/employee-detail.component.ts
index 0f871636..f036dc8d 100644
--- a/overlord/src/app/employee/employee-detail/employee-detail.component.ts
+++ b/overlord/src/app/employee/employee-detail/employee-detail.component.ts
@@ -1,5 +1,5 @@
import { Component, inject, afterNextRender, linkedSignal, input, computed } from '@angular/core';
-import { form as createForm, FormField, FormRoot } from '@angular/forms/signals';
+import { form as createForm, FormField, FormRoot, required } from '@angular/forms/signals';
import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatOptionModule } from '@angular/material/core';
@@ -28,8 +28,8 @@ export interface EmployeeDetailFormData {
points: number;
isActive: boolean;
costCentre: string;
- joiningDate: string;
- leavingDate: string | null;
+ joiningDate: Date;
+ leavingDate: Date | null;
}
@Component({
@@ -70,12 +70,22 @@ export class EmployeeDetailComponent {
points: itemVal.points || 0,
isActive: itemVal.isActive ?? true,
costCentre: itemVal.costCentre?.id ?? '',
- joiningDate: itemVal.joiningDate,
- leavingDate: itemVal.leavingDate,
+ joiningDate: itemVal.joiningDate ? moment(itemVal.joiningDate, 'DD-MMM-YYYY').toDate() : new Date(),
+ leavingDate: itemVal.leavingDate ? moment(itemVal.leavingDate, 'DD-MMM-YYYY').toDate() : null,
}),
});
- form = createForm(this.model);
+ form = createForm(this.model, (f) => {
+ required(f.name);
+ required(f.designation);
+ required(f.salary);
+ required(f.points);
+ required(f.joiningDate);
+ required(f.leavingDate, {
+ when: ({ valueOf }) => !valueOf(f.isActive),
+ message: 'Leaving date is required',
+ });
+ });
costCentresResource = this.costCentreService.list();
costCentres = computed(() => this.costCentresResource.value() || []);
@@ -87,6 +97,11 @@ export class EmployeeDetailComponent {
}
save() {
+ if (this.form().invalid()) {
+ const errorMsg = this.form().errorSummary()[0]?.message || 'Leaving date is required';
+ this.snackBar.open(errorMsg, 'Danger');
+ return;
+ }
this.ser.saveOrUpdate(this.getItem()).subscribe({
next: () => {
this.snackBar.open('', 'Success');
@@ -133,7 +148,8 @@ export class EmployeeDetailComponent {
item.isActive = formValue.isActive;
item.costCentre.id = formValue.costCentre ?? '';
item.joiningDate = moment(formValue.joiningDate).format('DD-MMM-YYYY');
- item.leavingDate = item.isActive ? null : moment(formValue.leavingDate).format('DD-MMM-YYYY');
+ item.leavingDate =
+ item.isActive || !formValue.leavingDate ? null : moment(formValue.leavingDate).format('DD-MMM-YYYY');
return item;
}
}
diff --git a/overlord/src/app/journal/journal.component.css b/overlord/src/app/journal/journal.component.css
index f4854f18..47106110 100644
--- a/overlord/src/app/journal/journal.component.css
+++ b/overlord/src/app/journal/journal.component.css
@@ -16,19 +16,242 @@
justify-content: center;
}
-.img-container {
+/* Attachments Section */
+.attachments-section {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ margin: 6px 0 16px;
+}
+
+.attachments-header {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ font-size: 13px;
+ font-weight: 500;
+ color: var(--mat-sys-on-surface-variant, #64748b);
+ user-select: none;
+}
+
+.attachments-header-icon {
+ font-size: 18px;
+ width: 18px;
+ height: 18px;
+ color: var(--mat-sys-primary, #0284c7);
+}
+
+.attachments-badge {
+ font-size: 11px;
+ font-weight: 600;
+ padding: 1px 7px;
+ border-radius: 9999px;
+ background: var(--mat-sys-secondary-container, #e2e8f0);
+ color: var(--mat-sys-on-secondary-container, #1e293b);
+}
+
+.attachments-grid {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: 16px;
+}
+
+.attachment-card {
position: relative;
+ width: 110px;
+ height: 155px;
+ border-radius: 12px;
+ overflow: hidden;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+ border: 1px solid var(--mat-sys-outline-variant, #e2e8f0);
+ background: var(--mat-sys-surface, #ffffff);
+ transition:
+ transform 0.2s ease,
+ box-shadow 0.2s ease;
}
-.img-container .overlay {
- display: none;
+.attachment-card:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12);
}
-.img-container:hover > .overlay {
- display: inline-block;
+.thumbnail-wrapper {
+ position: relative;
+ width: 100%;
+ height: 100%;
+ cursor: pointer;
+ outline: none;
+}
+
+.thumbnail-wrapper:focus-visible {
+ outline: 2px solid var(--mat-sys-primary, #0284c7);
+ outline-offset: -2px;
+}
+
+.thumbnail-img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ display: block;
+ transition: transform 0.25s ease;
+}
+
+.thumbnail-wrapper:hover .thumbnail-img {
+ transform: scale(1.05);
+}
+
+.zoom-overlay {
position: absolute;
- left: 60px;
- top: 0;
+ inset: 0;
+ background: rgba(0, 0, 0, 0.35);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ opacity: 0;
+ transition: opacity 0.2s ease;
+}
+
+.thumbnail-wrapper:hover .zoom-overlay,
+.thumbnail-wrapper:focus-visible .zoom-overlay {
+ opacity: 1;
+}
+
+.zoom-icon {
+ color: #ffffff;
+ font-size: 28px;
+ width: 28px;
+ height: 28px;
+ filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.5));
+}
+
+.attachment-card .delete-btn {
+ position: absolute;
+ top: 6px;
+ right: 6px;
+ width: 32px;
+ height: 32px;
+ min-width: 32px;
+ padding: 0;
+ background: rgba(255, 255, 255, 0.9);
+ backdrop-filter: blur(4px);
+ border-radius: 50%;
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
+ color: #dc2626;
+ opacity: 0;
+ transition:
+ opacity 0.2s ease,
+ background-color 0.2s ease,
+ color 0.2s ease,
+ transform 0.15s ease;
+ z-index: 3;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.attachment-card:hover .delete-btn,
+.attachment-card:focus-within .delete-btn {
+ opacity: 1;
+}
+
+.attachment-card .delete-btn:hover,
+.attachment-card .delete-btn:focus-visible {
+ background: #dc2626;
+ color: #ffffff;
+ transform: scale(1.08);
+}
+
+.attachment-card .delete-btn mat-icon {
+ font-size: 18px;
+ width: 18px;
+ height: 18px;
+ line-height: 18px;
+}
+
+.upload-tile {
+ width: 110px;
+ height: 155px;
+ border-radius: 12px;
+ border: 2px dashed var(--mat-sys-outline, #cbd5e1);
+ background: var(--mat-sys-surface-container-low, #f8fafc);
+ cursor: pointer;
+ box-sizing: border-box;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ text-decoration: none;
+ transition:
+ border-color 0.2s ease,
+ background-color 0.2s ease,
+ transform 0.2s ease;
+}
+
+.upload-tile:hover {
+ border-color: var(--mat-sys-primary, #0284c7);
+ background: rgba(2, 132, 199, 0.05);
+ transform: translateY(-2px);
+}
+
+.upload-tile:focus-visible {
+ outline: 2px solid var(--mat-sys-primary, #0284c7);
+ outline-offset: 2px;
+}
+
+.upload-content {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ text-align: center;
+ padding: 8px;
+ gap: 6px;
+}
+
+.upload-icon-circle {
+ width: 42px;
+ height: 42px;
+ border-radius: 50%;
+ background: var(--mat-sys-primary-container, #e0f2fe);
+ color: var(--mat-sys-on-primary-container, #0284c7);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: transform 0.2s ease;
+}
+
+.upload-tile:hover .upload-icon-circle {
+ transform: scale(1.08);
+}
+
+.upload-icon-circle mat-icon {
+ font-size: 22px;
+ width: 22px;
+ height: 22px;
+ line-height: 22px;
+}
+
+.upload-title {
+ font-size: 12px;
+ font-weight: 600;
+ color: var(--mat-sys-on-surface, #1e293b);
+}
+
+.upload-subtitle {
+ font-size: 10px;
+ color: var(--mat-sys-on-surface-variant, #64748b);
+}
+
+.file-input-hidden {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ white-space: nowrap;
+ border-width: 0;
}
.gold {
diff --git a/overlord/src/app/journal/journal.component.html b/overlord/src/app/journal/journal.component.html
index 2de866e4..43e90074 100644
--- a/overlord/src/app/journal/journal.component.html
+++ b/overlord/src/app/journal/journal.component.html
@@ -131,29 +131,70 @@
-
- @for (item of form.files().value(); track item) {
-
-
![img]()
-
-
- }
-
-
-
- @for (item of form.files().value(); track item) {
-
-
![img]()
-
-
- }
-
-
-
+
+
+
+
+ @for (item of form.files().value(); track item) {
+
+
+
![Attachment thumbnail]()
+
+ zoom_in
+
+
+
+
+ }
+
+
+
+
+ add_photo_alternate
+
+
Add Image
+
Click to upload
+
-
+
}
diff --git a/overlord/src/app/purchase-return/purchase-return.component.css b/overlord/src/app/purchase-return/purchase-return.component.css
index f4854f18..47106110 100644
--- a/overlord/src/app/purchase-return/purchase-return.component.css
+++ b/overlord/src/app/purchase-return/purchase-return.component.css
@@ -16,19 +16,242 @@
justify-content: center;
}
-.img-container {
+/* Attachments Section */
+.attachments-section {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ margin: 6px 0 16px;
+}
+
+.attachments-header {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ font-size: 13px;
+ font-weight: 500;
+ color: var(--mat-sys-on-surface-variant, #64748b);
+ user-select: none;
+}
+
+.attachments-header-icon {
+ font-size: 18px;
+ width: 18px;
+ height: 18px;
+ color: var(--mat-sys-primary, #0284c7);
+}
+
+.attachments-badge {
+ font-size: 11px;
+ font-weight: 600;
+ padding: 1px 7px;
+ border-radius: 9999px;
+ background: var(--mat-sys-secondary-container, #e2e8f0);
+ color: var(--mat-sys-on-secondary-container, #1e293b);
+}
+
+.attachments-grid {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: 16px;
+}
+
+.attachment-card {
position: relative;
+ width: 110px;
+ height: 155px;
+ border-radius: 12px;
+ overflow: hidden;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+ border: 1px solid var(--mat-sys-outline-variant, #e2e8f0);
+ background: var(--mat-sys-surface, #ffffff);
+ transition:
+ transform 0.2s ease,
+ box-shadow 0.2s ease;
}
-.img-container .overlay {
- display: none;
+.attachment-card:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12);
}
-.img-container:hover > .overlay {
- display: inline-block;
+.thumbnail-wrapper {
+ position: relative;
+ width: 100%;
+ height: 100%;
+ cursor: pointer;
+ outline: none;
+}
+
+.thumbnail-wrapper:focus-visible {
+ outline: 2px solid var(--mat-sys-primary, #0284c7);
+ outline-offset: -2px;
+}
+
+.thumbnail-img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ display: block;
+ transition: transform 0.25s ease;
+}
+
+.thumbnail-wrapper:hover .thumbnail-img {
+ transform: scale(1.05);
+}
+
+.zoom-overlay {
position: absolute;
- left: 60px;
- top: 0;
+ inset: 0;
+ background: rgba(0, 0, 0, 0.35);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ opacity: 0;
+ transition: opacity 0.2s ease;
+}
+
+.thumbnail-wrapper:hover .zoom-overlay,
+.thumbnail-wrapper:focus-visible .zoom-overlay {
+ opacity: 1;
+}
+
+.zoom-icon {
+ color: #ffffff;
+ font-size: 28px;
+ width: 28px;
+ height: 28px;
+ filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.5));
+}
+
+.attachment-card .delete-btn {
+ position: absolute;
+ top: 6px;
+ right: 6px;
+ width: 32px;
+ height: 32px;
+ min-width: 32px;
+ padding: 0;
+ background: rgba(255, 255, 255, 0.9);
+ backdrop-filter: blur(4px);
+ border-radius: 50%;
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
+ color: #dc2626;
+ opacity: 0;
+ transition:
+ opacity 0.2s ease,
+ background-color 0.2s ease,
+ color 0.2s ease,
+ transform 0.15s ease;
+ z-index: 3;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.attachment-card:hover .delete-btn,
+.attachment-card:focus-within .delete-btn {
+ opacity: 1;
+}
+
+.attachment-card .delete-btn:hover,
+.attachment-card .delete-btn:focus-visible {
+ background: #dc2626;
+ color: #ffffff;
+ transform: scale(1.08);
+}
+
+.attachment-card .delete-btn mat-icon {
+ font-size: 18px;
+ width: 18px;
+ height: 18px;
+ line-height: 18px;
+}
+
+.upload-tile {
+ width: 110px;
+ height: 155px;
+ border-radius: 12px;
+ border: 2px dashed var(--mat-sys-outline, #cbd5e1);
+ background: var(--mat-sys-surface-container-low, #f8fafc);
+ cursor: pointer;
+ box-sizing: border-box;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ text-decoration: none;
+ transition:
+ border-color 0.2s ease,
+ background-color 0.2s ease,
+ transform 0.2s ease;
+}
+
+.upload-tile:hover {
+ border-color: var(--mat-sys-primary, #0284c7);
+ background: rgba(2, 132, 199, 0.05);
+ transform: translateY(-2px);
+}
+
+.upload-tile:focus-visible {
+ outline: 2px solid var(--mat-sys-primary, #0284c7);
+ outline-offset: 2px;
+}
+
+.upload-content {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ text-align: center;
+ padding: 8px;
+ gap: 6px;
+}
+
+.upload-icon-circle {
+ width: 42px;
+ height: 42px;
+ border-radius: 50%;
+ background: var(--mat-sys-primary-container, #e0f2fe);
+ color: var(--mat-sys-on-primary-container, #0284c7);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: transform 0.2s ease;
+}
+
+.upload-tile:hover .upload-icon-circle {
+ transform: scale(1.08);
+}
+
+.upload-icon-circle mat-icon {
+ font-size: 22px;
+ width: 22px;
+ height: 22px;
+ line-height: 22px;
+}
+
+.upload-title {
+ font-size: 12px;
+ font-weight: 600;
+ color: var(--mat-sys-on-surface, #1e293b);
+}
+
+.upload-subtitle {
+ font-size: 10px;
+ color: var(--mat-sys-on-surface-variant, #64748b);
+}
+
+.file-input-hidden {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ white-space: nowrap;
+ border-width: 0;
}
.gold {
diff --git a/overlord/src/app/purchase-return/purchase-return.component.html b/overlord/src/app/purchase-return/purchase-return.component.html
index 7825f6e0..6bac1333 100644
--- a/overlord/src/app/purchase-return/purchase-return.component.html
+++ b/overlord/src/app/purchase-return/purchase-return.component.html
@@ -164,29 +164,70 @@
-
- @for (item of form.files().value(); track item) {
-
-
![img]()
-
-
- }
-
-
-
+
+
+
+
+ @for (item of form.files().value(); track item) {
+
+
+
![Attachment thumbnail]()
+
+ zoom_in
+
+
+
+
+ }
+
+
+
+
+ add_photo_alternate
+
+
Add Image
+
Click to upload
+
-
+
}
diff --git a/overlord/src/app/purchase/purchase.component.css b/overlord/src/app/purchase/purchase.component.css
index f4854f18..47106110 100644
--- a/overlord/src/app/purchase/purchase.component.css
+++ b/overlord/src/app/purchase/purchase.component.css
@@ -16,19 +16,242 @@
justify-content: center;
}
-.img-container {
+/* Attachments Section */
+.attachments-section {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ margin: 6px 0 16px;
+}
+
+.attachments-header {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ font-size: 13px;
+ font-weight: 500;
+ color: var(--mat-sys-on-surface-variant, #64748b);
+ user-select: none;
+}
+
+.attachments-header-icon {
+ font-size: 18px;
+ width: 18px;
+ height: 18px;
+ color: var(--mat-sys-primary, #0284c7);
+}
+
+.attachments-badge {
+ font-size: 11px;
+ font-weight: 600;
+ padding: 1px 7px;
+ border-radius: 9999px;
+ background: var(--mat-sys-secondary-container, #e2e8f0);
+ color: var(--mat-sys-on-secondary-container, #1e293b);
+}
+
+.attachments-grid {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: 16px;
+}
+
+.attachment-card {
position: relative;
+ width: 110px;
+ height: 155px;
+ border-radius: 12px;
+ overflow: hidden;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+ border: 1px solid var(--mat-sys-outline-variant, #e2e8f0);
+ background: var(--mat-sys-surface, #ffffff);
+ transition:
+ transform 0.2s ease,
+ box-shadow 0.2s ease;
}
-.img-container .overlay {
- display: none;
+.attachment-card:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12);
}
-.img-container:hover > .overlay {
- display: inline-block;
+.thumbnail-wrapper {
+ position: relative;
+ width: 100%;
+ height: 100%;
+ cursor: pointer;
+ outline: none;
+}
+
+.thumbnail-wrapper:focus-visible {
+ outline: 2px solid var(--mat-sys-primary, #0284c7);
+ outline-offset: -2px;
+}
+
+.thumbnail-img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ display: block;
+ transition: transform 0.25s ease;
+}
+
+.thumbnail-wrapper:hover .thumbnail-img {
+ transform: scale(1.05);
+}
+
+.zoom-overlay {
position: absolute;
- left: 60px;
- top: 0;
+ inset: 0;
+ background: rgba(0, 0, 0, 0.35);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ opacity: 0;
+ transition: opacity 0.2s ease;
+}
+
+.thumbnail-wrapper:hover .zoom-overlay,
+.thumbnail-wrapper:focus-visible .zoom-overlay {
+ opacity: 1;
+}
+
+.zoom-icon {
+ color: #ffffff;
+ font-size: 28px;
+ width: 28px;
+ height: 28px;
+ filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.5));
+}
+
+.attachment-card .delete-btn {
+ position: absolute;
+ top: 6px;
+ right: 6px;
+ width: 32px;
+ height: 32px;
+ min-width: 32px;
+ padding: 0;
+ background: rgba(255, 255, 255, 0.9);
+ backdrop-filter: blur(4px);
+ border-radius: 50%;
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
+ color: #dc2626;
+ opacity: 0;
+ transition:
+ opacity 0.2s ease,
+ background-color 0.2s ease,
+ color 0.2s ease,
+ transform 0.15s ease;
+ z-index: 3;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.attachment-card:hover .delete-btn,
+.attachment-card:focus-within .delete-btn {
+ opacity: 1;
+}
+
+.attachment-card .delete-btn:hover,
+.attachment-card .delete-btn:focus-visible {
+ background: #dc2626;
+ color: #ffffff;
+ transform: scale(1.08);
+}
+
+.attachment-card .delete-btn mat-icon {
+ font-size: 18px;
+ width: 18px;
+ height: 18px;
+ line-height: 18px;
+}
+
+.upload-tile {
+ width: 110px;
+ height: 155px;
+ border-radius: 12px;
+ border: 2px dashed var(--mat-sys-outline, #cbd5e1);
+ background: var(--mat-sys-surface-container-low, #f8fafc);
+ cursor: pointer;
+ box-sizing: border-box;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ text-decoration: none;
+ transition:
+ border-color 0.2s ease,
+ background-color 0.2s ease,
+ transform 0.2s ease;
+}
+
+.upload-tile:hover {
+ border-color: var(--mat-sys-primary, #0284c7);
+ background: rgba(2, 132, 199, 0.05);
+ transform: translateY(-2px);
+}
+
+.upload-tile:focus-visible {
+ outline: 2px solid var(--mat-sys-primary, #0284c7);
+ outline-offset: 2px;
+}
+
+.upload-content {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ text-align: center;
+ padding: 8px;
+ gap: 6px;
+}
+
+.upload-icon-circle {
+ width: 42px;
+ height: 42px;
+ border-radius: 50%;
+ background: var(--mat-sys-primary-container, #e0f2fe);
+ color: var(--mat-sys-on-primary-container, #0284c7);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: transform 0.2s ease;
+}
+
+.upload-tile:hover .upload-icon-circle {
+ transform: scale(1.08);
+}
+
+.upload-icon-circle mat-icon {
+ font-size: 22px;
+ width: 22px;
+ height: 22px;
+ line-height: 22px;
+}
+
+.upload-title {
+ font-size: 12px;
+ font-weight: 600;
+ color: var(--mat-sys-on-surface, #1e293b);
+}
+
+.upload-subtitle {
+ font-size: 10px;
+ color: var(--mat-sys-on-surface-variant, #64748b);
+}
+
+.file-input-hidden {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ white-space: nowrap;
+ border-width: 0;
}
.gold {
diff --git a/overlord/src/app/purchase/purchase.component.html b/overlord/src/app/purchase/purchase.component.html
index 27de296b..6e494b95 100644
--- a/overlord/src/app/purchase/purchase.component.html
+++ b/overlord/src/app/purchase/purchase.component.html
@@ -184,29 +184,70 @@
-
- @for (item of form.files().value(); track item) {
-
-
![img]()
-
-
- }
-
-
-
+
+
+
+
+ @for (item of form.files().value(); track item) {
+
+
+
![Attachment thumbnail]()
+
+ zoom_in
+
+
+
+
+ }
+
+
+
+
+ add_photo_alternate
+
+
Add Image
+
Click to upload
+
-
+
}
diff --git a/overlord/src/app/receipt/receipt.component.css b/overlord/src/app/receipt/receipt.component.css
index f4854f18..47106110 100644
--- a/overlord/src/app/receipt/receipt.component.css
+++ b/overlord/src/app/receipt/receipt.component.css
@@ -16,19 +16,242 @@
justify-content: center;
}
-.img-container {
+/* Attachments Section */
+.attachments-section {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ margin: 6px 0 16px;
+}
+
+.attachments-header {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ font-size: 13px;
+ font-weight: 500;
+ color: var(--mat-sys-on-surface-variant, #64748b);
+ user-select: none;
+}
+
+.attachments-header-icon {
+ font-size: 18px;
+ width: 18px;
+ height: 18px;
+ color: var(--mat-sys-primary, #0284c7);
+}
+
+.attachments-badge {
+ font-size: 11px;
+ font-weight: 600;
+ padding: 1px 7px;
+ border-radius: 9999px;
+ background: var(--mat-sys-secondary-container, #e2e8f0);
+ color: var(--mat-sys-on-secondary-container, #1e293b);
+}
+
+.attachments-grid {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: 16px;
+}
+
+.attachment-card {
position: relative;
+ width: 110px;
+ height: 155px;
+ border-radius: 12px;
+ overflow: hidden;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+ border: 1px solid var(--mat-sys-outline-variant, #e2e8f0);
+ background: var(--mat-sys-surface, #ffffff);
+ transition:
+ transform 0.2s ease,
+ box-shadow 0.2s ease;
}
-.img-container .overlay {
- display: none;
+.attachment-card:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12);
}
-.img-container:hover > .overlay {
- display: inline-block;
+.thumbnail-wrapper {
+ position: relative;
+ width: 100%;
+ height: 100%;
+ cursor: pointer;
+ outline: none;
+}
+
+.thumbnail-wrapper:focus-visible {
+ outline: 2px solid var(--mat-sys-primary, #0284c7);
+ outline-offset: -2px;
+}
+
+.thumbnail-img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ display: block;
+ transition: transform 0.25s ease;
+}
+
+.thumbnail-wrapper:hover .thumbnail-img {
+ transform: scale(1.05);
+}
+
+.zoom-overlay {
position: absolute;
- left: 60px;
- top: 0;
+ inset: 0;
+ background: rgba(0, 0, 0, 0.35);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ opacity: 0;
+ transition: opacity 0.2s ease;
+}
+
+.thumbnail-wrapper:hover .zoom-overlay,
+.thumbnail-wrapper:focus-visible .zoom-overlay {
+ opacity: 1;
+}
+
+.zoom-icon {
+ color: #ffffff;
+ font-size: 28px;
+ width: 28px;
+ height: 28px;
+ filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.5));
+}
+
+.attachment-card .delete-btn {
+ position: absolute;
+ top: 6px;
+ right: 6px;
+ width: 32px;
+ height: 32px;
+ min-width: 32px;
+ padding: 0;
+ background: rgba(255, 255, 255, 0.9);
+ backdrop-filter: blur(4px);
+ border-radius: 50%;
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
+ color: #dc2626;
+ opacity: 0;
+ transition:
+ opacity 0.2s ease,
+ background-color 0.2s ease,
+ color 0.2s ease,
+ transform 0.15s ease;
+ z-index: 3;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.attachment-card:hover .delete-btn,
+.attachment-card:focus-within .delete-btn {
+ opacity: 1;
+}
+
+.attachment-card .delete-btn:hover,
+.attachment-card .delete-btn:focus-visible {
+ background: #dc2626;
+ color: #ffffff;
+ transform: scale(1.08);
+}
+
+.attachment-card .delete-btn mat-icon {
+ font-size: 18px;
+ width: 18px;
+ height: 18px;
+ line-height: 18px;
+}
+
+.upload-tile {
+ width: 110px;
+ height: 155px;
+ border-radius: 12px;
+ border: 2px dashed var(--mat-sys-outline, #cbd5e1);
+ background: var(--mat-sys-surface-container-low, #f8fafc);
+ cursor: pointer;
+ box-sizing: border-box;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ text-decoration: none;
+ transition:
+ border-color 0.2s ease,
+ background-color 0.2s ease,
+ transform 0.2s ease;
+}
+
+.upload-tile:hover {
+ border-color: var(--mat-sys-primary, #0284c7);
+ background: rgba(2, 132, 199, 0.05);
+ transform: translateY(-2px);
+}
+
+.upload-tile:focus-visible {
+ outline: 2px solid var(--mat-sys-primary, #0284c7);
+ outline-offset: 2px;
+}
+
+.upload-content {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ text-align: center;
+ padding: 8px;
+ gap: 6px;
+}
+
+.upload-icon-circle {
+ width: 42px;
+ height: 42px;
+ border-radius: 50%;
+ background: var(--mat-sys-primary-container, #e0f2fe);
+ color: var(--mat-sys-on-primary-container, #0284c7);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: transform 0.2s ease;
+}
+
+.upload-tile:hover .upload-icon-circle {
+ transform: scale(1.08);
+}
+
+.upload-icon-circle mat-icon {
+ font-size: 22px;
+ width: 22px;
+ height: 22px;
+ line-height: 22px;
+}
+
+.upload-title {
+ font-size: 12px;
+ font-weight: 600;
+ color: var(--mat-sys-on-surface, #1e293b);
+}
+
+.upload-subtitle {
+ font-size: 10px;
+ color: var(--mat-sys-on-surface-variant, #64748b);
+}
+
+.file-input-hidden {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ white-space: nowrap;
+ border-width: 0;
}
.gold {
diff --git a/overlord/src/app/receipt/receipt.component.html b/overlord/src/app/receipt/receipt.component.html
index 8075bbaf..ddeca9e7 100644
--- a/overlord/src/app/receipt/receipt.component.html
+++ b/overlord/src/app/receipt/receipt.component.html
@@ -134,29 +134,70 @@
-
- @for (item of form.files().value(); track item) {
-
-
![img]()
-
-
- }
-
-
-
+
+
+
+
+ @for (item of form.files().value(); track item) {
+
+
+
![Attachment thumbnail]()
+
+ zoom_in
+
+
+
+
+ }
+
+
+
+
+ add_photo_alternate
+
+
Add Image
+
Click to upload
+
-
+
}
diff --git a/overlord/src/app/shared/image-dialog/image-dialog.component.html b/overlord/src/app/shared/image-dialog/image-dialog.component.html
index 94b2ef38..8143d930 100644
--- a/overlord/src/app/shared/image-dialog/image-dialog.component.html
+++ b/overlord/src/app/shared/image-dialog/image-dialog.component.html
@@ -1 +1 @@
-
+