Added file upload UI
This commit is contained in:
parent
c05cd770c6
commit
b15529008b
8
frontend/src/app/format-file-size-pipe.pipe.spec.ts
Normal file
8
frontend/src/app/format-file-size-pipe.pipe.spec.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { FormatFileSizePipePipe } from './format-file-size-pipe.pipe';
|
||||||
|
|
||||||
|
describe('FormatFileSizePipePipe', () => {
|
||||||
|
it('create an instance', () => {
|
||||||
|
const pipe = new FormatFileSizePipePipe();
|
||||||
|
expect(pipe).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
19
frontend/src/app/format-file-size-pipe.pipe.ts
Normal file
19
frontend/src/app/format-file-size-pipe.pipe.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { Pipe, PipeTransform } from '@angular/core';
|
||||||
|
|
||||||
|
@Pipe({
|
||||||
|
name: 'formatFileSizePipe',
|
||||||
|
standalone: true
|
||||||
|
})
|
||||||
|
export class FormatFileSizePipePipe implements PipeTransform {
|
||||||
|
|
||||||
|
transform(value: number, ...args: unknown[]): string {
|
||||||
|
if (value === null || value === 0) return '0 Bytes';
|
||||||
|
|
||||||
|
const units = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
||||||
|
const index = Math.min(Math.floor(Math.log(value) / Math.log(1024)), units.length - 1);
|
||||||
|
const filesize = Number((value / Math.pow(1024, index)).toFixed(2));
|
||||||
|
|
||||||
|
return `${filesize.toLocaleString()} ${units[index]}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,34 +1,63 @@
|
|||||||
<div class="container mx-auto p-4 mt-4">
|
<div class="container mx-auto p-4 mt-4">
|
||||||
<h1 class="text-4xl font-bold text-center text-gray-800 mb-10">Upload Your File</h1>
|
<h1 class="text-4xl font-bold text-center text-gray-800 mb-10">Upload Your File</h1>
|
||||||
<div class="bg-white shadow-lg rounded-lg p-6">
|
<div class="bg-white shadow-lg rounded-lg p-6">
|
||||||
<div class="flex flex-col items-center justify-center">
|
<div *ngIf="!uploadStarted" class="flex flex-col items-center justify-center">
|
||||||
<!-- File Drop Area -->
|
<!-- File Drop Area -->
|
||||||
<div class="border-2 border-dashed border-gray-300 rounded-lg w-full p-20 flex flex-col items-center justify-center mb-6">
|
<div
|
||||||
|
*ngIf="!fileToUpload && !uploadStarted"
|
||||||
|
(drop)="onDrop($event)"
|
||||||
|
(dragover)="onDragOver($event)"
|
||||||
|
(click)="fileInput.click()"
|
||||||
|
class="border-2 border-dashed border-gray-300 rounded-lg w-full p-20 flex flex-col items-center justify-center mb-6 animate-in fade-in duration-500">
|
||||||
<img class="w-16 mb-3" src="./assets/cloud-arrow-up-solid.svg">
|
<img class="w-16 mb-3" src="./assets/cloud-arrow-up-solid.svg">
|
||||||
<p class="text-gray-700 mb-3">Drag and drop your files here or <span class="text-blue-600 cursor-pointer">browse</span></p>
|
<p class="text-gray-700 mb-3">Drag and drop your files here or <span class="text-blue-600 cursor-pointer">browse</span></p>
|
||||||
<input type="file" class="hidden" />
|
<input
|
||||||
|
#fileInput
|
||||||
|
(change)="onFileSelected($event)"
|
||||||
|
type="file" class="hidden" />
|
||||||
|
</div>
|
||||||
|
<!-- File Details -->
|
||||||
|
<div
|
||||||
|
*ngIf="fileToUpload && !uploadStarted"
|
||||||
|
class="w-full p-10 flex flex-col items-center justify-center mb-3 animate-in fade-in duration-500">
|
||||||
|
<img class="w-14 mb-3" src="./assets/file-solid.svg">
|
||||||
|
<p class="text-gray-700 mb-3"> {{ fileToUpload.name }} </p>
|
||||||
|
<p class="text-gray-700 mb-3"> {{ fileToUpload.size | formatFileSizePipe }} </p>
|
||||||
</div>
|
</div>
|
||||||
<!-- Settings -->
|
<!-- Settings -->
|
||||||
<div class="w-full mb-10">
|
<div class="w-full mb-10">
|
||||||
<label class="block text-gray-700 text-l font-bold mb-4">Options</label>
|
<label class="block text-gray-700 text-l font-bold mb-4">Options</label>
|
||||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<input type="checkbox" class="toggle" checked />
|
<input id="shortStorage" [(ngModel)]="shortStorage" type="checkbox" class="toggle" checked />
|
||||||
<span class="ml-2 text-gray-700">Store for only one hour</span>
|
<label for="shortStorage" class="ml-2 text-gray-700">Store for only one hour</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<input type="checkbox" class="toggle" />
|
<input id="singleDownload" [(ngModel)]="singleDownload" type="checkbox" class="toggle" />
|
||||||
<span class="ml-2 text-gray-700">Allow only one download</span>
|
<label for="singleDownload" class="ml-2 text-gray-700">Allow only one download</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<input type="checkbox" class="toggle" />
|
<input id="passwordProtected" [(ngModel)]="passwordProtected" type="checkbox" class="toggle" />
|
||||||
<span class="ml-2 text-gray-700">Protect download with password</span>
|
<label for="passwordProtected" class="ml-2 text-gray-700">Protect download with password</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Upload Button -->
|
<!-- Upload Button -->
|
||||||
<button class="btn btn-primary w-full max-w-xs">Upload Files</button>
|
<button class="btn btn-primary w-full max-w-xs" (click)="startUpload()">Upload File</button>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="uploadStarted && fileToUpload && !uploadFinished" class="flex flex-col items-center justify-center animate-in fade-in duration-500">
|
||||||
|
<div class="radial-progress" [style.--value]="uploadProgress" style="--size:12rem; --thickness: 2rem;" role="progressbar">{{uploadProgress}}%</div>
|
||||||
|
<p class="text-gray-700 mt-4">Uploading {{ fileToUpload.name }}...</p>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="uploadFinished" class="flex flex-col items-center justify-center animate-in fade-in duration-1000">
|
||||||
|
<img class="w-16 mb-3" src="./assets/circle-check-solid.svg">
|
||||||
|
<p class="text-gray-700 mb-3">File uploaded successfully!</p>
|
||||||
|
<button class="btn btn-primary w-full max-w-xs">Upload Another File</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p class="text-gray-600 text-center mt-6">Drag and drop files, or click to select the files you want to share.</p>
|
<p class="text-gray-600 text-center mt-6">Drag and drop files, or click to select the files you want to share.</p>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Invisible SVGs to prevent lazy loading -->
|
||||||
|
<div class="invisible">
|
||||||
|
<img src="./assets/file-solid.svg">
|
||||||
|
</div>
|
||||||
|
@ -1,12 +1,78 @@
|
|||||||
import { Component } from '@angular/core';
|
import {Component, ElementRef, ViewChild} from '@angular/core';
|
||||||
|
import {NgIf} from "@angular/common";
|
||||||
|
import {FormatFileSizePipePipe} from "../format-file-size-pipe.pipe";
|
||||||
|
import {FormsModule} from "@angular/forms";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-upload',
|
selector: 'app-upload',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [],
|
imports: [
|
||||||
|
NgIf,
|
||||||
|
FormatFileSizePipePipe,
|
||||||
|
FormsModule
|
||||||
|
],
|
||||||
templateUrl: './upload.component.html',
|
templateUrl: './upload.component.html',
|
||||||
styleUrl: './upload.component.scss'
|
styleUrl: './upload.component.scss'
|
||||||
})
|
})
|
||||||
export class UploadComponent {
|
export class UploadComponent {
|
||||||
|
@ViewChild('fileInput') fileInput!: ElementRef;
|
||||||
|
|
||||||
|
fileToUpload: File | null = null;
|
||||||
|
|
||||||
|
shortStorage: boolean = false;
|
||||||
|
singleDownload: boolean = false;
|
||||||
|
passwordProtected: boolean = false;
|
||||||
|
password: string = '';
|
||||||
|
|
||||||
|
uploadStarted: boolean = false;
|
||||||
|
uploadProgress = 0;
|
||||||
|
uploadFinished = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages the file input click or drag and drop
|
||||||
|
*/
|
||||||
|
onFileSelected(event: any): void {
|
||||||
|
const files = event.target.files;
|
||||||
|
if (files && files.length > 0) {
|
||||||
|
this.handleFiles(files);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onDragOver(event: DragEvent): void {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
// Optionally add visual feedback
|
||||||
|
}
|
||||||
|
onDrop(event: DragEvent): void {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
const files = event.dataTransfer?.files;
|
||||||
|
if (files && files.length > 0) {
|
||||||
|
this.handleFiles(files);
|
||||||
|
}
|
||||||
|
// Remove visual feedback
|
||||||
|
}
|
||||||
|
handleFiles(files: FileList): void {
|
||||||
|
for(let i = 0; i < files.length; i++) {
|
||||||
|
const file = files[i];
|
||||||
|
this.fileToUpload = file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
startUpload(): void {
|
||||||
|
if (this.fileToUpload) {
|
||||||
|
console.log(`Uploading file: ${this.fileToUpload.name}, Short storage: ${this.shortStorage}, Single download: ${this.singleDownload}, Password protected: ${this.passwordProtected}`);
|
||||||
|
this.uploadStarted = true;
|
||||||
|
|
||||||
|
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
if (this.uploadProgress < 100) {
|
||||||
|
this.uploadProgress++;
|
||||||
|
} else {
|
||||||
|
clearInterval(interval); // Stop the interval when progress reaches 100%
|
||||||
|
this.uploadFinished = true;
|
||||||
|
}
|
||||||
|
}, 10);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
1
frontend/src/assets/circle-check-solid.svg
Normal file
1
frontend/src/assets/circle-check-solid.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path fill="#2b3440" d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM369 209L241 337c-9.4 9.4-24.6 9.4-33.9 0l-64-64c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0l47 47L335 175c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9z"/></svg>
|
After Width: | Height: | Size: 440 B |
1
frontend/src/assets/file-solid.svg
Normal file
1
frontend/src/assets/file-solid.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path fill="#2b3440" d="M0 64C0 28.7 28.7 0 64 0H224V128c0 17.7 14.3 32 32 32H384V448c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V64zm384 64H256V0L384 128z"/></svg>
|
After Width: | Height: | Size: 380 B |
Loading…
x
Reference in New Issue
Block a user