140 lines
3.9 KiB
TypeScript
140 lines
3.9 KiB
TypeScript
import { ui_locales } from "@ui/ui_template_locale";
|
|
|
|
export type FileSlot = "las_laz" | "prj" | "tfw" | "tif" | "dxf";
|
|
export type UploadStatus = "pending" | "uploading" | "completed" | "failed";
|
|
|
|
export interface SlotConfig {
|
|
slot: FileSlot;
|
|
labelKey: keyof typeof ui_locales;
|
|
icon: string;
|
|
extensions: readonly string[];
|
|
isRequired: boolean;
|
|
}
|
|
|
|
export interface FileSlotState extends SlotConfig {
|
|
file?: File;
|
|
uploadSessionId?: string;
|
|
uploadStatus: UploadStatus;
|
|
progressBytes: number;
|
|
speedMbs: number;
|
|
etaSeconds: number | null;
|
|
error?: string;
|
|
}
|
|
|
|
export interface StoredUploadSession {
|
|
key: string;
|
|
projectId: string;
|
|
slot: FileSlot;
|
|
fileName: string;
|
|
fileSize: number;
|
|
uploadSessionId: string;
|
|
chunkSizeBytes: number;
|
|
totalChunks: number;
|
|
completedChunks: number;
|
|
updatedAt: number;
|
|
}
|
|
|
|
const SLOT_CONFIGS: readonly SlotConfig[] = [
|
|
{
|
|
slot: "las_laz",
|
|
labelKey: "B03_File_Slot_PointCloud",
|
|
icon: "●",
|
|
extensions: [".las", ".laz"],
|
|
isRequired: true,
|
|
},
|
|
{
|
|
slot: "prj",
|
|
labelKey: "B03_File_Slot_Projection",
|
|
icon: "◇",
|
|
extensions: [".prj"],
|
|
isRequired: true,
|
|
},
|
|
{
|
|
slot: "tfw",
|
|
labelKey: "B03_File_Slot_RasterCoord",
|
|
icon: "□",
|
|
extensions: [".tfw"],
|
|
isRequired: true,
|
|
},
|
|
{
|
|
slot: "tif",
|
|
labelKey: "B03_File_Slot_TerrainDem",
|
|
icon: "▧",
|
|
extensions: [".tif"],
|
|
isRequired: false,
|
|
},
|
|
];
|
|
|
|
export function getExtension(fileName: string): string {
|
|
const index = fileName.lastIndexOf(".");
|
|
return index >= 0 ? fileName.slice(index).toLowerCase() : "";
|
|
}
|
|
|
|
export function formatBytes(bytes: number): string {
|
|
const gb = bytes / 1024 / 1024 / 1024;
|
|
if (gb >= 1) return `${gb.toFixed(2)} GB`;
|
|
return `${(bytes / 1024 / 1024).toFixed(2)} MB`;
|
|
}
|
|
|
|
export function formatEta(seconds: number | null): string {
|
|
if (seconds === null || !Number.isFinite(seconds)) return "-";
|
|
if (seconds < 60) return `${Math.ceil(seconds)}s`;
|
|
return `${Math.ceil(seconds / 60)}m`;
|
|
}
|
|
|
|
export function makeSessionKey(projectId: string, file: File): string {
|
|
return `b03_upload_${projectId}_${file.name}_${file.size}`;
|
|
}
|
|
|
|
export function initializeSlots(): Map<FileSlot, FileSlotState> {
|
|
const map = new Map<FileSlot, FileSlotState>();
|
|
for (const config of SLOT_CONFIGS) {
|
|
map.set(config.slot, {
|
|
...config,
|
|
uploadStatus: "pending",
|
|
progressBytes: 0,
|
|
speedMbs: 0,
|
|
etaSeconds: null,
|
|
});
|
|
}
|
|
return map;
|
|
}
|
|
|
|
export function createFileCardTemplate(): HTMLTemplateElement {
|
|
const template = document.createElement("template");
|
|
template.id = "file-card-template";
|
|
template.innerHTML = `
|
|
<article class="b03-file__card b03-file__card--empty">
|
|
<div class="b03-file__card-header">
|
|
<span class="b03-file__card-icon" aria-hidden="true"></span>
|
|
<div class="b03-file__card-heading">
|
|
<strong class="b03-file__card-label"></strong>
|
|
<span class="b03-file__card-ext"></span>
|
|
</div>
|
|
<div class="b03-file__card-badge-container"></div>
|
|
<button class="b03-file__card-remove" type="button"></button>
|
|
</div>
|
|
<div class="b03-file__card-content">
|
|
<button class="b03-file__card-select" type="button"></button>
|
|
<input class="b03-file__slot-input" type="file" />
|
|
<div class="b03-file__file-info">
|
|
<span class="b03-file__file-name"></span>
|
|
<span class="b03-file__file-size"></span>
|
|
</div>
|
|
<div class="b03-file__progress-section">
|
|
<div class="b03-file__progress-bar-container">
|
|
<div class="b03-file__progress-bar"></div>
|
|
</div>
|
|
<div class="b03-file__progress-info">
|
|
<span class="b03-file__progress-bytes"></span>
|
|
<span class="b03-file__progress-speed"></span>
|
|
<span class="b03-file__progress-eta"></span>
|
|
</div>
|
|
</div>
|
|
<div class="b03-file__error-message" role="alert"></div>
|
|
</div>
|
|
</article>
|
|
`;
|
|
return template;
|
|
}
|