Move SealShare onto Livewire Material
Replaces maryUI and daisyUI with nonameweb/livewire-material: the Vibrant indigo scheme, a system/light/dark theme under sealshare-theme, one top app bar with the account menu, the upload drop zone and link-ready moments, M3 fields, dialogs instead of wire:confirm, snackbars instead of flashed messages, a sortable admin table, and the starter-kit cleanup. Docker builds assets after Composer; CI drops the Flux step. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V9NnLxnPp8vaaurb3Z1MFy
This commit is contained in:
co-authored by
Claude Opus 5
parent
6d7120e8e2
commit
c4a17b65c8
@@ -1,21 +1,16 @@
|
||||
<div class="max-w-3xl mx-auto">
|
||||
<div class="text-center mb-6">
|
||||
<div class="mx-auto max-w-3xl">
|
||||
<div class="mb-8 text-center">
|
||||
@if ($siteLogo)
|
||||
<img src="{{ Storage::disk('public')->url($siteLogo) }}" alt="{{ $siteTitle ?: config('app.name', 'SealShare') }}" class="h-20 w-auto mx-auto mb-4" />
|
||||
@else
|
||||
<x-app-logo-icon class="h-16 w-16 mx-auto mb-4" />
|
||||
<img src="{{ Storage::disk('public')->url($siteLogo) }}" alt="{{ $siteTitle ?: config('app.name', 'SealShare') }}" class="mx-auto mb-4 h-20 w-auto" />
|
||||
@endif
|
||||
|
||||
<h1 class="text-2xl font-bold">{{ $siteTitle ?: config('app.name', 'SealShare') }}</h1>
|
||||
<h1 class="type-headline-lg">{{ $siteTitle ?: config('app.name', 'SealShare') }}</h1>
|
||||
|
||||
<p class="mt-2 opacity-70">{{ $siteDescription ?: __('Share your files safely and securely') }}</p>
|
||||
<p class="mt-2 type-body-lg text-on-surface-variant">{{ $siteDescription ?: __('Share your files safely and securely') }}</p>
|
||||
</div>
|
||||
|
||||
@if ($isStorageFull)
|
||||
<div class="alert alert-warning mb-6">
|
||||
<x-icon name="o-exclamation-triangle" class="w-5 h-5" />
|
||||
<span>{{ __('Storage is full. Uploads are temporarily disabled.') }}</span>
|
||||
</div>
|
||||
<x-alert color="warning" :title="__('Storage is full. Uploads are temporarily disabled.')" />
|
||||
@else
|
||||
<form
|
||||
wire:submit="createShare"
|
||||
@@ -88,97 +83,98 @@
|
||||
x-on:livewire-upload-error="resetUpload()"
|
||||
x-on:livewire-upload-progress="progress = $event.detail.progress"
|
||||
>
|
||||
{{-- Drop Zone --}}
|
||||
{{-- Drop zone: the shape behind the icon turns into a burst while files are over it. --}}
|
||||
<div
|
||||
class="border-2 border-dashed rounded-xl p-8 text-center transition-colors mb-6"
|
||||
:class="{
|
||||
'border-primary bg-primary/5': dragging,
|
||||
'border-base-300 hover:border-primary/50': !dragging,
|
||||
'opacity-50 pointer-events-none': uploading
|
||||
class="mb-6 rounded-corner-xl border-2 border-dashed p-8 text-center transition-colors duration-(--md-sys-motion-effects-default-duration) ease-effects-default"
|
||||
x-bind:class="{
|
||||
'border-primary bg-primary-container/40': dragging,
|
||||
'border-outline-variant': ! dragging,
|
||||
'pointer-events-none opacity-60': uploading,
|
||||
}"
|
||||
@dragover.prevent="dragging = true"
|
||||
@dragleave.prevent="dragging = false"
|
||||
@drop.prevent="handleDrop($event)"
|
||||
x-on:dragover.prevent="dragging = true"
|
||||
x-on:dragleave.prevent="dragging = false"
|
||||
x-on:drop.prevent="handleDrop($event)"
|
||||
data-test="drop-zone"
|
||||
>
|
||||
<x-icon name="o-cloud-arrow-up" class="w-12 h-12 mx-auto opacity-40 mb-3" />
|
||||
<p class="font-medium">{{ __('Drag & drop files or folders here') }}</p>
|
||||
<p class="text-sm opacity-60 mt-1">{{ __('or click to browse') }}</p>
|
||||
<div class="relative mx-auto mb-4 grid size-28 place-items-center">
|
||||
<span
|
||||
class="absolute inset-0 transition-[scale,rotate,opacity] duration-(--md-sys-motion-spatial-slow-duration) ease-spatial-slow motion-reduce:transition-none"
|
||||
x-bind:class="dragging ? 'scale-50 rotate-45 opacity-0' : 'scale-100 rotate-0 opacity-100'"
|
||||
><x-shape name="cookie-9" class="size-full text-secondary-container" /></span>
|
||||
<span
|
||||
class="absolute inset-0 transition-[scale,rotate,opacity] duration-(--md-sys-motion-spatial-slow-duration) ease-spatial-slow motion-reduce:transition-none"
|
||||
x-bind:class="dragging ? 'scale-110 rotate-0 opacity-100' : 'scale-50 -rotate-45 opacity-0'"
|
||||
><x-shape name="soft-burst" class="size-full text-primary-container" /></span>
|
||||
<x-icon name="upload" class="relative size-12 text-on-secondary-container" x-bind:class="dragging && 'text-on-primary-container'" />
|
||||
</div>
|
||||
|
||||
<label class="btn btn-outline btn-sm mt-4 cursor-pointer" :class="uploading && 'btn-disabled'">
|
||||
<p class="type-title-md">{{ __('Drag & drop files or folders here') }}</p>
|
||||
<p class="mt-1 type-body-md text-on-surface-variant">{{ __('or click to browse') }}</p>
|
||||
|
||||
<label
|
||||
class="state-layer focus-ring mt-4 inline-flex h-10 cursor-pointer items-center gap-2 rounded-corner-full border border-outline-variant px-4 type-label-lg text-primary has-focus-visible:outline-3 has-focus-visible:outline-secondary"
|
||||
x-bind:class="uploading && 'pointer-events-none opacity-38'"
|
||||
>
|
||||
<x-icon name="folder_open" class="size-5" />
|
||||
{{ __('Browse Files') }}
|
||||
<input
|
||||
type="file"
|
||||
wire:model="files"
|
||||
multiple
|
||||
class="hidden"
|
||||
:disabled="uploading"
|
||||
/>
|
||||
<input type="file" wire:model="files" multiple class="sr-only" x-bind:disabled="uploading" />
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{{-- Upload Progress --}}
|
||||
<div x-show="uploading" x-cloak class="mb-6">
|
||||
<template x-if="progress < 100">
|
||||
<div>
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<span class="text-sm font-medium">{{ __('Uploading...') }} <span x-text="Math.round(progress)"></span>%</span>
|
||||
<button type="button" class="btn btn-ghost btn-xs" x-on:click="$wire.cancelUpload('files')">
|
||||
{{ __('Cancel') }}
|
||||
</button>
|
||||
</div>
|
||||
<progress class="progress progress-primary w-full" max="100" x-bind:value="progress"></progress>
|
||||
{{-- Upload progress --}}
|
||||
<div x-show="uploading" x-cloak class="mb-6" data-test="upload-progress">
|
||||
<div x-show="progress < 100">
|
||||
<div class="mb-2 flex items-center justify-between">
|
||||
<span class="type-label-lg">{{ __('Uploading...') }} <span x-text="Math.round(progress)"></span>%</span>
|
||||
<x-button :label="__('Cancel')" size="xs" x-on:click="$wire.cancelUpload('files')" />
|
||||
</div>
|
||||
</template>
|
||||
<template x-if="progress >= 100">
|
||||
<div class="flex items-center gap-3 text-sm font-medium">
|
||||
<span class="loading loading-spinner loading-sm"></span>
|
||||
{{ __('Processing files...') }}
|
||||
</div>
|
||||
</template>
|
||||
<x-progress bind="progress" wavy :label="__('Uploading')" />
|
||||
</div>
|
||||
<div x-show="progress >= 100" class="flex items-center gap-3 type-label-lg">
|
||||
<x-loading class="size-8" :label="false" />
|
||||
{{ __('Processing files...') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Errors --}}
|
||||
@error('files')
|
||||
<div class="alert alert-error mb-4">{{ $message }}</div>
|
||||
<x-alert color="error" class="mb-4">{{ $message }}</x-alert>
|
||||
@enderror
|
||||
|
||||
{{-- File List --}}
|
||||
{{-- Selected files --}}
|
||||
@if (count($files))
|
||||
<div class="mb-6">
|
||||
<h3 class="font-semibold mb-2">{{ __('Selected Files') }} ({{ count($files) }})</h3>
|
||||
<div class="space-y-1 max-h-60 overflow-y-auto">
|
||||
@foreach ($files as $index => $file)
|
||||
<div class="flex items-center justify-between px-3 py-2 rounded-lg bg-base-200 text-sm">
|
||||
<div class="flex items-center gap-2 min-w-0">
|
||||
<x-icon name="o-document" class="w-4 h-4 flex-shrink-0" />
|
||||
<span class="truncate">
|
||||
{{ $relativePaths[$index] ?? $file->getClientOriginalName() }}
|
||||
</span>
|
||||
<span class="opacity-50 flex-shrink-0">
|
||||
({{ Number::fileSize($file->getSize()) }})
|
||||
</span>
|
||||
</div>
|
||||
<button type="button" wire:click="removeFile({{ $index }})" class="btn btn-ghost btn-xs">
|
||||
<x-icon name="o-x-mark" class="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
@endforeach
|
||||
<h2 class="mb-2 type-title-md">{{ __('Selected Files') }} ({{ count($files) }})</h2>
|
||||
<div class="max-h-72 overflow-y-auto">
|
||||
<x-list segmented :label="__('Selected Files')">
|
||||
@foreach ($files as $index => $file)
|
||||
<x-list-item
|
||||
:title="$relativePaths[$index] ?? $file->getClientOriginalName()"
|
||||
:description="Number::fileSize($file->getSize())"
|
||||
icon="description"
|
||||
wire:key="selected-file-{{ $index }}"
|
||||
>
|
||||
<x-slot:end>
|
||||
<x-button icon="close" :aria-label="__('Remove')" wire:click="removeFile({{ $index }})" />
|
||||
</x-slot:end>
|
||||
</x-list-item>
|
||||
@endforeach
|
||||
</x-list>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Options --}}
|
||||
<x-card title="{{ __('Share Options') }}" class="mb-6" shadow>
|
||||
<div class="space-y-4">
|
||||
<x-toggle wire:model.live="usePassword" label="{{ __('Password protect') }}" />
|
||||
<x-card :title="__('Share Options')" variant="outlined" class="mb-6">
|
||||
<div class="grid gap-5">
|
||||
<x-toggle wire:model.live="usePassword" :label="__('Password protect')" right />
|
||||
|
||||
@if ($usePassword)
|
||||
<x-password wire:model="password" label="{{ __('Password') }}" />
|
||||
<x-password wire:model="password" :label="__('Password')" autocomplete="new-password" />
|
||||
@endif
|
||||
|
||||
<x-select
|
||||
wire:model="expiration"
|
||||
label="{{ __('Expiration') }}"
|
||||
:label="__('Expiration')"
|
||||
:placeholder="$allowNeverExpire ? __('Never') : null"
|
||||
:options="[
|
||||
['id' => '1h', 'name' => __('1 Hour')],
|
||||
@@ -192,22 +188,24 @@
|
||||
|
||||
<x-input
|
||||
wire:model="maxDownloads"
|
||||
label="{{ __('Max downloads') }}"
|
||||
:label="__('Max downloads')"
|
||||
type="number"
|
||||
min="1"
|
||||
placeholder="{{ __('Unlimited') }}"
|
||||
:placeholder="__('Unlimited')"
|
||||
/>
|
||||
</div>
|
||||
</x-card>
|
||||
|
||||
{{-- Submit --}}
|
||||
<x-button
|
||||
type="submit"
|
||||
label="{{ __('Create Share Link') }}"
|
||||
class="btn-primary w-full"
|
||||
icon="o-link"
|
||||
:label="__('Create Share Link')"
|
||||
variant="filled"
|
||||
size="md"
|
||||
class="w-full"
|
||||
icon="link"
|
||||
spinner="createShare"
|
||||
x-bind:disabled="uploading || {{ count($files) === 0 ? 'true' : 'false' }}"
|
||||
data-test="create-share"
|
||||
/>
|
||||
</form>
|
||||
@endif
|
||||
|
||||
Reference in New Issue
Block a user