Skip to content

Commit 5af8e78

Browse files
authored
Merge pull request #3483 from codeeu/dev
training paeg backend eits
2 parents ded6875 + f364149 commit 5af8e78

File tree

6 files changed

+89
-130
lines changed

6 files changed

+89
-130
lines changed

app/Nova/Actions/BulkUploadMediaFiles.php

Lines changed: 24 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,15 @@
33
namespace App\Nova\Actions;
44

55
use App\MediaUpload;
6-
use DigitalCreative\Filepond\Filepond;
7-
use DigitalCreative\Filepond\Data\Data;
86
use Illuminate\Bus\Queueable;
9-
use Illuminate\Http\UploadedFile;
7+
use Illuminate\Http\Request;
108
use Illuminate\Queue\InteractsWithQueue;
119
use Illuminate\Queue\SerializesModels;
1210
use Illuminate\Support\Collection;
13-
use Illuminate\Support\Facades\Log;
14-
use Illuminate\Support\Facades\Storage;
1511
use Illuminate\Support\Str;
1612
use Laravel\Nova\Actions\Action;
1713
use Laravel\Nova\Fields\ActionFields;
14+
use Laravel\Nova\Fields\File;
1815
use Laravel\Nova\Http\Requests\NovaRequest;
1916

2017
class BulkUploadMediaFiles extends Action
@@ -37,7 +34,7 @@ class BulkUploadMediaFiles extends Action
3734
*/
3835
public function handle(ActionFields $fields, Collection $models)
3936
{
40-
$uploadedFiles = $this->collectUploadedFiles($fields);
37+
$uploadedFiles = $this->collectUploadedFiles($fields, request());
4138
if (empty($uploadedFiles)) {
4239
return Action::danger('Please select one or more files.');
4340
}
@@ -56,11 +53,7 @@ public function handle(ActionFields $fields, Collection $models)
5653
continue;
5754
}
5855

59-
$originalName = $this->extractOriginalName($file);
60-
if ($originalName === null) {
61-
$skipped++;
62-
continue;
63-
}
56+
$originalName = $file->getClientOriginalName();
6457
$extension = strtolower((string) pathinfo($originalName, PATHINFO_EXTENSION));
6558
if (!in_array($extension, $allowedExtensions, true)) {
6659
$skipped++;
@@ -71,7 +64,7 @@ public function handle(ActionFields $fields, Collection $models)
7164
$safeBaseName = Str::slug($baseName) ?: 'upload-file';
7265
$destination = 'nova/uploads/' . now()->format('Y/m');
7366
$finalFileName = $safeBaseName . '-' . Str::random(8) . '.' . $extension;
74-
$storedPath = $this->storeFile($file, $destination, $finalFileName);
67+
$storedPath = $file->storeAs($destination, $finalFileName, 'resources');
7568

7669
if (!$storedPath) {
7770
$skipped++;
@@ -98,123 +91,34 @@ public function handle(ActionFields $fields, Collection $models)
9891
*/
9992
public function fields(NovaRequest $request): array
10093
{
101-
return [
102-
Filepond::make('Files', 'files')
103-
->multiple()
104-
->limit(50)
105-
->acceptedTypes('.jpg,.jpeg,.png,.gif,.webp,.svg,.pdf,.doc,.docx,.ppt,.pptx,.xls,.xlsx,.txt')
106-
->rules('required')
107-
->help('Drag and drop multiple files or click to select. Supported: images, PDF, Office docs, and TXT.'),
108-
];
109-
}
110-
111-
/**
112-
* Collect uploaded files from Nova action fields + raw request payload.
113-
*
114-
* @return UploadedFile[]
115-
*/
116-
protected function collectUploadedFiles(ActionFields $fields): array
117-
{
118-
$candidates = [];
119-
120-
if (isset($fields->files)) {
121-
$candidates[] = $fields->files;
122-
}
94+
$fields = [];
12395

124-
$requestItems = request()->collect('files')->all();
125-
if (!empty($requestItems)) {
126-
$candidates[] = $requestItems;
96+
for ($i = 1; $i <= 20; $i++) {
97+
$fields[] = File::make("File {$i}", "file_{$i}")
98+
->rules('nullable', 'max:51200', 'mimes:jpg,jpeg,png,gif,webp,svg,pdf,doc,docx,ppt,pptx,xls,xlsx,txt');
12799
}
128100

129-
$requestFiles = request()->allFiles();
130-
if (!empty($requestFiles)) {
131-
$candidates[] = $requestFiles;
132-
}
133-
134-
$flatten = function ($value) use (&$flatten): array {
135-
if ($value instanceof UploadedFile) {
136-
return [$value];
137-
}
138-
139-
if (is_string($value)) {
140-
return [$value];
141-
}
142-
143-
if (!is_array($value)) {
144-
return [];
145-
}
146-
147-
$result = [];
148-
foreach ($value as $item) {
149-
$result = array_merge($result, $flatten($item));
150-
}
101+
$fields[0]->help('Upload up to 20 files in one run (mix of images/docs).');
151102

152-
return $result;
153-
};
154-
155-
$files = [];
156-
foreach ($candidates as $candidate) {
157-
$files = array_merge($files, $flatten($candidate));
158-
}
159-
160-
return $files;
161-
}
162-
163-
protected function extractOriginalName(UploadedFile|string $file): ?string
164-
{
165-
if ($file instanceof UploadedFile) {
166-
return $file->getClientOriginalName();
167-
}
168-
169-
try {
170-
$data = Data::fromEncrypted($file);
171-
return $data->filename;
172-
} catch (\Throwable $e) {
173-
return null;
174-
}
103+
return $fields;
175104
}
176105

177-
protected function storeFile(UploadedFile|string $file, string $destination, string $finalFileName): ?string
106+
/**
107+
* Collect uploaded files from explicit action slots.
108+
*
109+
* @return array
110+
*/
111+
protected function collectUploadedFiles(ActionFields $fields, Request $request): array
178112
{
179-
if ($file instanceof UploadedFile) {
180-
return $file->storeAs($destination, $finalFileName, 'resources');
181-
}
182-
183-
try {
184-
$data = Data::fromEncrypted($file);
185-
} catch (\Throwable $e) {
186-
return null;
187-
}
188-
189-
$targetPath = $destination . '/' . $finalFileName;
190-
$stream = Storage::disk($data->disk)->readStream($data->path);
191-
192-
if (is_resource($stream)) {
193-
try {
194-
$stored = Storage::disk('resources')->writeStream($targetPath, $stream, ['visibility' => 'public']);
195-
} finally {
196-
fclose($stream);
197-
}
198-
} else {
199-
// Some environments return null instead of a stream for temp files.
200-
// Fallback to reading file contents directly.
201-
$contents = Storage::disk($data->disk)->get($data->path);
202-
if (!is_string($contents) || $contents === '') {
203-
Log::warning('[BulkUploadMediaFiles] Unable to read temp file contents', [
204-
'disk' => $data->disk,
205-
'path' => $data->path,
206-
]);
207-
return null;
113+
$files = [];
114+
for ($i = 1; $i <= 20; $i++) {
115+
$key = "file_{$i}";
116+
$file = $fields->{$key} ?? $request->file($key);
117+
if ($file) {
118+
$files[] = $file;
208119
}
209-
210-
$stored = Storage::disk('resources')->put($targetPath, $contents, 'public');
211120
}
212121

213-
// Clean only this temp file. Deleting the whole directory can remove
214-
// sibling files from the same multi-upload batch.
215-
$data->deleteFile();
216-
217-
return $stored ? $targetPath : null;
122+
return $files;
218123
}
219-
220124
}

app/Nova/TrainingResource.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,12 @@ public function fields(Request $request): array
138138
->nullable()
139139
->rules('nullable', 'url'),
140140

141+
Text::make('Third button text', 'third_button_text')->nullable(),
142+
143+
Text::make('Third button URL', 'third_button_url')
144+
->nullable()
145+
->rules('nullable', 'url'),
146+
141147
Text::make('Meta title', 'meta_title')
142148
->nullable()
143149
->help('Optional HTML title override'),

app/TrainingResource.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ class TrainingResource extends Model
3232
'button_url',
3333
'secondary_button_text',
3434
'secondary_button_url',
35+
'third_button_text',
36+
'third_button_url',
3537
'meta_title',
3638
'meta_description',
3739
'position',
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
return new class extends Migration
8+
{
9+
/**
10+
* Run the migrations.
11+
*/
12+
public function up(): void
13+
{
14+
Schema::table('training_resources', function (Blueprint $table) {
15+
$table->string('third_button_text')->nullable()->after('secondary_button_url');
16+
$table->string('third_button_url')->nullable()->after('third_button_text');
17+
});
18+
}
19+
20+
/**
21+
* Reverse the migrations.
22+
*/
23+
public function down(): void
24+
{
25+
Schema::table('training_resources', function (Blueprint $table) {
26+
$table->dropColumn([
27+
'third_button_text',
28+
'third_button_url',
29+
]);
30+
});
31+
}
32+
};

resources/views/static/training/index.blade.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ class="absolute top-0 right-0 h-full max-w-[calc(70vw)] object-cover hidden md:b
289289
@foreach($dynamicResults as $result)
290290
<div class="flex overflow-hidden flex-col bg-white rounded-lg cursor-pointer" onclick="window.location.href='{{ $result['link'] }}'">
291291
<div class="relative">
292-
<img src="{{ $result['image'] }}" class="w-full" />
292+
<img src="{{ $result['image'] }}" class="w-full md:h-[244px] md:object-cover" />
293293
</div>
294294
<div class="block flex-grow px-6 py-8">
295295
<p class="text-dark-blue text-lg p-0 font-semibold mb-2 font-['Montserrat']">
@@ -306,7 +306,7 @@ class="absolute top-0 right-0 h-full max-w-[calc(70vw)] object-cover hidden md:b
306306
@foreach($results as $result)
307307
<div class="flex overflow-hidden flex-col bg-white rounded-lg cursor-pointer" onclick="window.location.href='{{ $result['link'] }}'">
308308
<div class="relative">
309-
<img src="{{ $result['image'] }}" class="w-full" />
309+
<img src="{{ $result['image'] }}" class="w-full md:h-[244px] md:object-cover" />
310310
</div>
311311
<div class="block flex-grow px-6 py-8">
312312
<p class="text-dark-blue text-lg p-0 font-semibold mb-2 font-['Montserrat']">

resources/views/training/show.blade.php

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -88,14 +88,29 @@ class="mb-12 w-full h-full max-h-[630px] object-contain"
8888
@endif
8989

9090
@if(!empty($trainingResource->button_text) && !empty($trainingResource->button_url))
91-
<a
92-
class="max-xl:!hidden inline-block bg-[#F95C22] rounded-full py-2.5 px-6 font-['Blinker'] hover:bg-hover-orange duration-300 text-base font-semibold leading-7 text-black normal-case"
93-
href="{{ $trainingResource->button_url }}"
94-
target="_blank"
95-
rel="noopener noreferrer"
96-
>
97-
{{ $trainingResource->button_text }}
98-
</a>
91+
<div>
92+
<a
93+
class="max-xl:!hidden inline-block bg-[#F95C22] rounded-full py-2.5 px-6 font-['Blinker'] hover:bg-hover-orange duration-300 text-base font-semibold leading-7 text-black normal-case"
94+
href="{{ $trainingResource->button_url }}"
95+
target="_blank"
96+
rel="noopener noreferrer"
97+
>
98+
{{ $trainingResource->button_text }}
99+
</a>
100+
101+
@if(!empty($trainingResource->third_button_text) && !empty($trainingResource->third_button_url))
102+
<div class="mt-4">
103+
<a
104+
class="max-xl:!hidden inline-block bg-[#F95C22] rounded-full py-2.5 px-6 font-['Blinker'] hover:bg-hover-orange duration-300 text-base font-semibold leading-7 text-black normal-case"
105+
href="{{ $trainingResource->third_button_url }}"
106+
target="_blank"
107+
rel="noopener noreferrer"
108+
>
109+
{{ $trainingResource->third_button_text }}
110+
</a>
111+
</div>
112+
@endif
113+
</div>
99114
@endif
100115

101116
@if(!empty($trainingResource->contacts_section))

0 commit comments

Comments
 (0)