807 lines
23 KiB
TypeScript
807 lines
23 KiB
TypeScript
/**
|
|
* @file sdig-contract-attachments.ts
|
|
* @description Contract attachments and prior contracts manager
|
|
*/
|
|
|
|
import {
|
|
DeesElement,
|
|
property,
|
|
html,
|
|
customElement,
|
|
type TemplateResult,
|
|
css,
|
|
cssManager,
|
|
state,
|
|
} from '@design.estate/dees-element';
|
|
|
|
import * as plugins from '../../plugins.js';
|
|
|
|
declare global {
|
|
interface HTMLElementTagNameMap {
|
|
'sdig-contract-attachments': SdigContractAttachments;
|
|
}
|
|
}
|
|
|
|
// Attachment interface
|
|
interface IAttachment {
|
|
id: string;
|
|
name: string;
|
|
type: 'document' | 'image' | 'spreadsheet' | 'pdf' | 'other';
|
|
mimeType: string;
|
|
size: number;
|
|
uploadedAt: number;
|
|
uploadedBy: string;
|
|
description?: string;
|
|
url?: string;
|
|
}
|
|
|
|
// File type configuration
|
|
const FILE_TYPES = {
|
|
document: { icon: 'lucide:FileText', color: '#3b82f6', label: 'Document' },
|
|
image: { icon: 'lucide:Image', color: '#10b981', label: 'Image' },
|
|
spreadsheet: { icon: 'lucide:Sheet', color: '#22c55e', label: 'Spreadsheet' },
|
|
pdf: { icon: 'lucide:FileType', color: '#ef4444', label: 'PDF' },
|
|
other: { icon: 'lucide:File', color: '#6b7280', label: 'File' },
|
|
};
|
|
|
|
@customElement('sdig-contract-attachments')
|
|
export class SdigContractAttachments extends DeesElement {
|
|
// ============================================================================
|
|
// STATIC
|
|
// ============================================================================
|
|
|
|
public static demo = () => html`
|
|
<sdig-contract-attachments
|
|
.contract=${plugins.sdDemodata.demoContract}
|
|
></sdig-contract-attachments>
|
|
`;
|
|
|
|
public static styles = [
|
|
cssManager.defaultStyles,
|
|
css`
|
|
:host {
|
|
display: block;
|
|
}
|
|
|
|
.attachments-container {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 24px;
|
|
}
|
|
|
|
/* Section card */
|
|
.section-card {
|
|
background: ${cssManager.bdTheme('#ffffff', '#0a0a0a')};
|
|
border: 1px solid ${cssManager.bdTheme('#e5e5e5', '#27272a')};
|
|
border-radius: 12px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.section-header {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 16px 20px;
|
|
background: ${cssManager.bdTheme('#f9fafb', '#111111')};
|
|
border-bottom: 1px solid ${cssManager.bdTheme('#e5e5e5', '#27272a')};
|
|
}
|
|
|
|
.section-title {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
font-size: 15px;
|
|
font-weight: 600;
|
|
color: ${cssManager.bdTheme('#111111', '#fafafa')};
|
|
}
|
|
|
|
.section-title dees-icon {
|
|
font-size: 18px;
|
|
color: ${cssManager.bdTheme('#6b7280', '#9ca3af')};
|
|
}
|
|
|
|
.section-count {
|
|
font-size: 13px;
|
|
color: ${cssManager.bdTheme('#6b7280', '#9ca3af')};
|
|
}
|
|
|
|
.section-content {
|
|
padding: 20px;
|
|
}
|
|
|
|
/* Upload zone */
|
|
.upload-zone {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 40px 20px;
|
|
border: 2px dashed ${cssManager.bdTheme('#d1d5db', '#3f3f46')};
|
|
border-radius: 12px;
|
|
background: ${cssManager.bdTheme('#f9fafb', '#111111')};
|
|
cursor: pointer;
|
|
transition: all 0.15s ease;
|
|
}
|
|
|
|
.upload-zone:hover {
|
|
border-color: ${cssManager.bdTheme('#9ca3af', '#52525b')};
|
|
background: ${cssManager.bdTheme('#f3f4f6', '#18181b')};
|
|
}
|
|
|
|
.upload-zone.dragging {
|
|
border-color: ${cssManager.bdTheme('#3b82f6', '#60a5fa')};
|
|
background: ${cssManager.bdTheme('#eff6ff', '#172554')};
|
|
}
|
|
|
|
.upload-zone-icon {
|
|
width: 64px;
|
|
height: 64px;
|
|
border-radius: 16px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 28px;
|
|
background: ${cssManager.bdTheme('#e5e7eb', '#27272a')};
|
|
color: ${cssManager.bdTheme('#6b7280', '#9ca3af')};
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.upload-zone-title {
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
color: ${cssManager.bdTheme('#111111', '#fafafa')};
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.upload-zone-subtitle {
|
|
font-size: 14px;
|
|
color: ${cssManager.bdTheme('#6b7280', '#9ca3af')};
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.upload-zone-hint {
|
|
font-size: 12px;
|
|
color: ${cssManager.bdTheme('#9ca3af', '#6b7280')};
|
|
}
|
|
|
|
/* Attachments list */
|
|
.attachments-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 12px;
|
|
}
|
|
|
|
.attachment-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 14px;
|
|
padding: 14px 16px;
|
|
background: ${cssManager.bdTheme('#f9fafb', '#111111')};
|
|
border: 1px solid ${cssManager.bdTheme('#e5e5e5', '#27272a')};
|
|
border-radius: 10px;
|
|
transition: all 0.15s ease;
|
|
}
|
|
|
|
.attachment-item:hover {
|
|
border-color: ${cssManager.bdTheme('#d1d5db', '#3f3f46')};
|
|
}
|
|
|
|
.attachment-icon {
|
|
width: 44px;
|
|
height: 44px;
|
|
border-radius: 10px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 20px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.attachment-info {
|
|
flex: 1;
|
|
min-width: 0;
|
|
}
|
|
|
|
.attachment-name {
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
color: ${cssManager.bdTheme('#111111', '#fafafa')};
|
|
margin-bottom: 4px;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.attachment-meta {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 12px;
|
|
font-size: 12px;
|
|
color: ${cssManager.bdTheme('#6b7280', '#9ca3af')};
|
|
}
|
|
|
|
.attachment-meta-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
}
|
|
|
|
.attachment-actions {
|
|
display: flex;
|
|
gap: 4px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
/* Prior contracts */
|
|
.prior-contracts-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 12px;
|
|
}
|
|
|
|
.prior-contract-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 14px;
|
|
padding: 16px;
|
|
background: ${cssManager.bdTheme('#f9fafb', '#111111')};
|
|
border: 1px solid ${cssManager.bdTheme('#e5e5e5', '#27272a')};
|
|
border-radius: 10px;
|
|
cursor: pointer;
|
|
transition: all 0.15s ease;
|
|
}
|
|
|
|
.prior-contract-item:hover {
|
|
border-color: ${cssManager.bdTheme('#d1d5db', '#3f3f46')};
|
|
background: ${cssManager.bdTheme('#f3f4f6', '#18181b')};
|
|
}
|
|
|
|
.prior-contract-icon {
|
|
width: 48px;
|
|
height: 48px;
|
|
border-radius: 10px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 22px;
|
|
background: ${cssManager.bdTheme('#dbeafe', '#1e3a5f')};
|
|
color: ${cssManager.bdTheme('#3b82f6', '#60a5fa')};
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.prior-contract-info {
|
|
flex: 1;
|
|
min-width: 0;
|
|
}
|
|
|
|
.prior-contract-title {
|
|
font-size: 15px;
|
|
font-weight: 600;
|
|
color: ${cssManager.bdTheme('#111111', '#fafafa')};
|
|
margin-bottom: 4px;
|
|
}
|
|
|
|
.prior-contract-context {
|
|
font-size: 13px;
|
|
color: ${cssManager.bdTheme('#6b7280', '#9ca3af')};
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.prior-contract-actions {
|
|
display: flex;
|
|
gap: 8px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
/* Empty state */
|
|
.empty-state {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
padding: 40px 20px;
|
|
text-align: center;
|
|
color: ${cssManager.bdTheme('#6b7280', '#9ca3af')};
|
|
}
|
|
|
|
.empty-state dees-icon {
|
|
font-size: 48px;
|
|
margin-bottom: 16px;
|
|
opacity: 0.5;
|
|
}
|
|
|
|
.empty-state h4 {
|
|
margin: 0 0 8px;
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
color: ${cssManager.bdTheme('#374151', '#d1d5db')};
|
|
}
|
|
|
|
.empty-state p {
|
|
margin: 0;
|
|
font-size: 14px;
|
|
}
|
|
|
|
/* Storage summary */
|
|
.storage-summary {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 16px;
|
|
padding: 16px;
|
|
background: ${cssManager.bdTheme('#f9fafb', '#111111')};
|
|
border: 1px solid ${cssManager.bdTheme('#e5e5e5', '#27272a')};
|
|
border-radius: 10px;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.storage-info {
|
|
flex: 1;
|
|
}
|
|
|
|
.storage-label {
|
|
font-size: 13px;
|
|
color: ${cssManager.bdTheme('#6b7280', '#9ca3af')};
|
|
margin-bottom: 6px;
|
|
}
|
|
|
|
.storage-bar {
|
|
height: 6px;
|
|
background: ${cssManager.bdTheme('#e5e7eb', '#27272a')};
|
|
border-radius: 3px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.storage-fill {
|
|
height: 100%;
|
|
background: ${cssManager.bdTheme('#3b82f6', '#60a5fa')};
|
|
transition: width 0.3s ease;
|
|
}
|
|
|
|
.storage-text {
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
color: ${cssManager.bdTheme('#111111', '#fafafa')};
|
|
white-space: nowrap;
|
|
}
|
|
|
|
/* Buttons */
|
|
.btn {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
padding: 8px 14px;
|
|
font-size: 13px;
|
|
font-weight: 500;
|
|
border-radius: 6px;
|
|
border: none;
|
|
cursor: pointer;
|
|
transition: all 0.15s ease;
|
|
}
|
|
|
|
.btn-sm {
|
|
padding: 6px 10px;
|
|
font-size: 12px;
|
|
}
|
|
|
|
.btn-primary {
|
|
background: ${cssManager.bdTheme('#111111', '#fafafa')};
|
|
color: ${cssManager.bdTheme('#ffffff', '#09090b')};
|
|
}
|
|
|
|
.btn-primary:hover {
|
|
background: ${cssManager.bdTheme('#333333', '#e5e5e5')};
|
|
}
|
|
|
|
.btn-secondary {
|
|
background: ${cssManager.bdTheme('#f3f4f6', '#27272a')};
|
|
color: ${cssManager.bdTheme('#374151', '#d1d5db')};
|
|
}
|
|
|
|
.btn-secondary:hover {
|
|
background: ${cssManager.bdTheme('#e5e7eb', '#3f3f46')};
|
|
}
|
|
|
|
.btn-ghost {
|
|
background: transparent;
|
|
color: ${cssManager.bdTheme('#6b7280', '#9ca3af')};
|
|
padding: 6px;
|
|
}
|
|
|
|
.btn-ghost:hover {
|
|
background: ${cssManager.bdTheme('#f3f4f6', '#27272a')};
|
|
color: ${cssManager.bdTheme('#111111', '#fafafa')};
|
|
}
|
|
|
|
.btn-danger {
|
|
color: ${cssManager.bdTheme('#dc2626', '#f87171')};
|
|
}
|
|
|
|
.btn-danger:hover {
|
|
background: ${cssManager.bdTheme('#fef2f2', '#450a0a')};
|
|
}
|
|
|
|
/* Type badge */
|
|
.type-badge {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
padding: 2px 8px;
|
|
border-radius: 4px;
|
|
font-size: 11px;
|
|
font-weight: 500;
|
|
background: ${cssManager.bdTheme('#e5e7eb', '#27272a')};
|
|
color: ${cssManager.bdTheme('#374151', '#d1d5db')};
|
|
}
|
|
`,
|
|
];
|
|
|
|
// ============================================================================
|
|
// PROPERTIES
|
|
// ============================================================================
|
|
|
|
@property({ type: Object })
|
|
public accessor contract: plugins.sdInterfaces.IPortableContract | null = null;
|
|
|
|
@property({ type: Boolean })
|
|
public accessor readonly: boolean = false;
|
|
|
|
// ============================================================================
|
|
// STATE
|
|
// ============================================================================
|
|
|
|
@state()
|
|
private accessor isDragging: boolean = false;
|
|
|
|
// Demo attachments data
|
|
@state()
|
|
private accessor attachments: IAttachment[] = [
|
|
{
|
|
id: '1',
|
|
name: 'Employment_Terms_v2.pdf',
|
|
type: 'pdf',
|
|
mimeType: 'application/pdf',
|
|
size: 245760,
|
|
uploadedAt: Date.now() - 86400000 * 3,
|
|
uploadedBy: 'employer',
|
|
description: 'Original employment terms document',
|
|
},
|
|
{
|
|
id: '2',
|
|
name: 'ID_Verification.png',
|
|
type: 'image',
|
|
mimeType: 'image/png',
|
|
size: 1024000,
|
|
uploadedAt: Date.now() - 86400000,
|
|
uploadedBy: 'employee',
|
|
},
|
|
{
|
|
id: '3',
|
|
name: 'Tax_Information.xlsx',
|
|
type: 'spreadsheet',
|
|
mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
size: 52480,
|
|
uploadedAt: Date.now() - 86400000 * 2,
|
|
uploadedBy: 'employer',
|
|
},
|
|
];
|
|
|
|
// ============================================================================
|
|
// EVENT HANDLERS
|
|
// ============================================================================
|
|
|
|
private handleFieldChange(path: string, value: unknown) {
|
|
this.dispatchEvent(
|
|
new CustomEvent('field-change', {
|
|
detail: { path, value },
|
|
bubbles: true,
|
|
composed: true,
|
|
})
|
|
);
|
|
}
|
|
|
|
private handleDragEnter(e: DragEvent) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
this.isDragging = true;
|
|
}
|
|
|
|
private handleDragLeave(e: DragEvent) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
this.isDragging = false;
|
|
}
|
|
|
|
private handleDragOver(e: DragEvent) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
}
|
|
|
|
private handleDrop(e: DragEvent) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
this.isDragging = false;
|
|
|
|
const files = e.dataTransfer?.files;
|
|
if (files && files.length > 0) {
|
|
this.handleFiles(files);
|
|
}
|
|
}
|
|
|
|
private handleFileSelect() {
|
|
const input = document.createElement('input');
|
|
input.type = 'file';
|
|
input.multiple = true;
|
|
input.onchange = () => {
|
|
if (input.files && input.files.length > 0) {
|
|
this.handleFiles(input.files);
|
|
}
|
|
};
|
|
input.click();
|
|
}
|
|
|
|
private handleFiles(files: FileList) {
|
|
// Demo: just add to list
|
|
Array.from(files).forEach((file) => {
|
|
const newAttachment: IAttachment = {
|
|
id: `att-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
name: file.name,
|
|
type: this.getFileType(file.type),
|
|
mimeType: file.type,
|
|
size: file.size,
|
|
uploadedAt: Date.now(),
|
|
uploadedBy: 'user',
|
|
};
|
|
this.attachments = [...this.attachments, newAttachment];
|
|
});
|
|
}
|
|
|
|
private handleDeleteAttachment(attachmentId: string) {
|
|
this.attachments = this.attachments.filter((a) => a.id !== attachmentId);
|
|
}
|
|
|
|
private handleAddPriorContract() {
|
|
// TODO: Open prior contract picker modal
|
|
}
|
|
|
|
private handleRemovePriorContract(index: number) {
|
|
if (!this.contract) return;
|
|
const updatedPriorContracts = [...this.contract.priorContracts];
|
|
updatedPriorContracts.splice(index, 1);
|
|
this.handleFieldChange('priorContracts', updatedPriorContracts);
|
|
}
|
|
|
|
// ============================================================================
|
|
// HELPERS
|
|
// ============================================================================
|
|
|
|
private getFileType(mimeType: string): IAttachment['type'] {
|
|
if (mimeType.includes('pdf')) return 'pdf';
|
|
if (mimeType.includes('image')) return 'image';
|
|
if (mimeType.includes('spreadsheet') || mimeType.includes('excel')) return 'spreadsheet';
|
|
if (mimeType.includes('document') || mimeType.includes('word')) return 'document';
|
|
return 'other';
|
|
}
|
|
|
|
private getFileTypeConfig(type: IAttachment['type']) {
|
|
return FILE_TYPES[type] || FILE_TYPES.other;
|
|
}
|
|
|
|
private formatFileSize(bytes: number): string {
|
|
if (bytes === 0) return '0 B';
|
|
const k = 1024;
|
|
const sizes = ['B', 'KB', 'MB', 'GB'];
|
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
|
|
}
|
|
|
|
private formatDate(timestamp: number): string {
|
|
return new Date(timestamp).toLocaleDateString('en-US', {
|
|
month: 'short',
|
|
day: 'numeric',
|
|
year: 'numeric',
|
|
});
|
|
}
|
|
|
|
private getTotalSize(): number {
|
|
return this.attachments.reduce((sum, a) => sum + a.size, 0);
|
|
}
|
|
|
|
private getPartyName(roleId: string): string {
|
|
const role = this.contract?.availableRoles.find((r) => r.id === roleId);
|
|
return role?.name || roleId;
|
|
}
|
|
|
|
// ============================================================================
|
|
// RENDER
|
|
// ============================================================================
|
|
|
|
public render(): TemplateResult {
|
|
if (!this.contract) {
|
|
return html`<div>No contract loaded</div>`;
|
|
}
|
|
|
|
const totalSize = this.getTotalSize();
|
|
const maxSize = 50 * 1024 * 1024; // 50MB demo limit
|
|
const usagePercent = Math.min((totalSize / maxSize) * 100, 100);
|
|
|
|
return html`
|
|
<div class="attachments-container">
|
|
<!-- Attachments Section -->
|
|
<div class="section-card">
|
|
<div class="section-header">
|
|
<div class="section-title">
|
|
<dees-icon .icon=${'lucide:Paperclip'}></dees-icon>
|
|
Attachments
|
|
</div>
|
|
<span class="section-count">${this.attachments.length} files</span>
|
|
</div>
|
|
<div class="section-content">
|
|
<!-- Storage summary -->
|
|
<div class="storage-summary">
|
|
<div class="storage-info">
|
|
<div class="storage-label">Storage used</div>
|
|
<div class="storage-bar">
|
|
<div class="storage-fill" style="width: ${usagePercent}%"></div>
|
|
</div>
|
|
</div>
|
|
<div class="storage-text">
|
|
${this.formatFileSize(totalSize)} / ${this.formatFileSize(maxSize)}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Upload zone -->
|
|
${!this.readonly
|
|
? html`
|
|
<div
|
|
class="upload-zone ${this.isDragging ? 'dragging' : ''}"
|
|
@dragenter=${this.handleDragEnter}
|
|
@dragleave=${this.handleDragLeave}
|
|
@dragover=${this.handleDragOver}
|
|
@drop=${this.handleDrop}
|
|
@click=${this.handleFileSelect}
|
|
>
|
|
<div class="upload-zone-icon">
|
|
<dees-icon .icon=${'lucide:UploadCloud'}></dees-icon>
|
|
</div>
|
|
<div class="upload-zone-title">Drop files here or click to upload</div>
|
|
<div class="upload-zone-subtitle">Add supporting documents, images, or spreadsheets</div>
|
|
<div class="upload-zone-hint">PDF, DOCX, XLSX, PNG, JPG up to 10MB each</div>
|
|
</div>
|
|
`
|
|
: ''}
|
|
|
|
<!-- Attachments list -->
|
|
${this.attachments.length > 0
|
|
? html`
|
|
<div class="attachments-list" style="margin-top: 20px;">
|
|
${this.attachments.map((attachment) => this.renderAttachmentItem(attachment))}
|
|
</div>
|
|
`
|
|
: html`
|
|
<div class="empty-state" style="margin-top: 20px;">
|
|
<dees-icon .icon=${'lucide:FileX'}></dees-icon>
|
|
<h4>No Attachments</h4>
|
|
<p>Upload files to attach them to this contract</p>
|
|
</div>
|
|
`}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Prior Contracts Section -->
|
|
<div class="section-card">
|
|
<div class="section-header">
|
|
<div class="section-title">
|
|
<dees-icon .icon=${'lucide:Files'}></dees-icon>
|
|
Prior Contracts
|
|
</div>
|
|
${!this.readonly
|
|
? html`
|
|
<button class="btn btn-secondary" @click=${this.handleAddPriorContract}>
|
|
<dees-icon .icon=${'lucide:Plus'}></dees-icon>
|
|
Link Contract
|
|
</button>
|
|
`
|
|
: ''}
|
|
</div>
|
|
<div class="section-content">
|
|
${this.contract.priorContracts.length > 0
|
|
? html`
|
|
<div class="prior-contracts-list">
|
|
${this.contract.priorContracts.map((priorContract, index) =>
|
|
this.renderPriorContractItem(priorContract, index)
|
|
)}
|
|
</div>
|
|
`
|
|
: html`
|
|
<div class="empty-state">
|
|
<dees-icon .icon=${'lucide:Link'}></dees-icon>
|
|
<h4>No Prior Contracts</h4>
|
|
<p>Link related or predecessor contracts here</p>
|
|
</div>
|
|
`}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
private renderAttachmentItem(attachment: IAttachment): TemplateResult {
|
|
const typeConfig = this.getFileTypeConfig(attachment.type);
|
|
|
|
return html`
|
|
<div class="attachment-item">
|
|
<div class="attachment-icon" style="background: ${typeConfig.color}20; color: ${typeConfig.color}">
|
|
<dees-icon .icon=${typeConfig.icon}></dees-icon>
|
|
</div>
|
|
<div class="attachment-info">
|
|
<div class="attachment-name">${attachment.name}</div>
|
|
<div class="attachment-meta">
|
|
<span class="type-badge">${typeConfig.label}</span>
|
|
<span class="attachment-meta-item">
|
|
${this.formatFileSize(attachment.size)}
|
|
</span>
|
|
<span class="attachment-meta-item">
|
|
<dees-icon .icon=${'lucide:Calendar'}></dees-icon>
|
|
${this.formatDate(attachment.uploadedAt)}
|
|
</span>
|
|
<span class="attachment-meta-item">
|
|
<dees-icon .icon=${'lucide:User'}></dees-icon>
|
|
${this.getPartyName(attachment.uploadedBy)}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div class="attachment-actions">
|
|
<button class="btn btn-ghost" title="Download">
|
|
<dees-icon .icon=${'lucide:Download'}></dees-icon>
|
|
</button>
|
|
<button class="btn btn-ghost" title="Preview">
|
|
<dees-icon .icon=${'lucide:Eye'}></dees-icon>
|
|
</button>
|
|
${!this.readonly
|
|
? html`
|
|
<button
|
|
class="btn btn-ghost btn-danger"
|
|
title="Delete"
|
|
@click=${() => this.handleDeleteAttachment(attachment.id)}
|
|
>
|
|
<dees-icon .icon=${'lucide:Trash2'}></dees-icon>
|
|
</button>
|
|
`
|
|
: ''}
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
private renderPriorContractItem(priorContract: plugins.sdInterfaces.IPortableContract, index: number): TemplateResult {
|
|
return html`
|
|
<div class="prior-contract-item">
|
|
<div class="prior-contract-icon">
|
|
<dees-icon .icon=${'lucide:FileText'}></dees-icon>
|
|
</div>
|
|
<div class="prior-contract-info">
|
|
<div class="prior-contract-title">${priorContract.title}</div>
|
|
<div class="prior-contract-context">${priorContract.context || 'No description'}</div>
|
|
</div>
|
|
<div class="prior-contract-actions">
|
|
<button class="btn btn-secondary btn-sm">
|
|
<dees-icon .icon=${'lucide:ExternalLink'}></dees-icon>
|
|
View
|
|
</button>
|
|
${!this.readonly
|
|
? html`
|
|
<button
|
|
class="btn btn-ghost btn-danger"
|
|
@click=${() => this.handleRemovePriorContract(index)}
|
|
>
|
|
<dees-icon .icon=${'lucide:Unlink'}></dees-icon>
|
|
</button>
|
|
`
|
|
: ''}
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
}
|