Skip to content

Commit 2ca945d

Browse files
authored
Merge pull request #176 from velstorelabs/feature/admin-validation-v2
Prefill brand logo on form validation failure and save correctly to DB
2 parents 71eea2a + 989eb25 commit 2ca945d

File tree

1 file changed

+123
-80
lines changed

1 file changed

+123
-80
lines changed
Lines changed: 123 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,89 @@
11
@extends('admin.layouts.admin')
22
@section('content')
3-
<div class="card mt-4">
4-
<div class="card-header card-header-bg text-white">
5-
<h6 class="d-flex align-items-center mb-0 dt-heading">{{ __('cms.brands.heading') }}
6-
</h6>
7-
</div>
8-
<div class="card-body">
9-
<form action="{{ route('admin.brands.store') }}" method="POST" enctype="multipart/form-data">
10-
@csrf
11-
<div class="row">
12-
<ul class="nav nav-tabs" id="languageTabs" role="tablist">
13-
@foreach($activeLanguages as $language)
14-
<li class="nav-item" role="presentation">
15-
<button class="nav-link {{ $loop->first ? 'active' : '' }}" id="{{ $language->name }}-tab" data-bs-toggle="tab" data-bs-target="#{{ $language->name }}" type="button" role="tab">{{ ucwords($language->name) }}</button>
16-
</li>
17-
@endforeach
18-
</ul>
19-
20-
<div class="tab-content mt-3" id="languageTabContent">
21-
@foreach($activeLanguages as $language)
22-
<div class="tab-pane fade show {{ $loop->first ? 'active' : '' }}" id="{{ $language->name }}" role="tabpanel">
23-
<label class="form-label">{{ __('cms.brands.name') }} ({{ $language->code }})</label>
24-
<input type="text"
25-
name="translations[{{ $language->code }}][name]"
26-
class="form-control @error("translations.{$language->code}.name") is-invalid @enderror"
27-
value="{{ old("translations.{$language->code}.name") }}">
28-
@error("translations.{$language->code}.name")
29-
<div class="invalid-feedback d-block">{{ $message }}</div>
30-
@enderror
31-
32-
<label class="form-label mt-3">{{ __('cms.brands.description') }} ({{ $language->code }})</label>
33-
<textarea name="translations[{{ $language->code }}][description]"
34-
class="form-control ck-editor-multi-languages @error("translations.{$language->code}.description") is-invalid @enderror">{{ old("translations.{$language->code}.description") }}</textarea>
35-
@error("translations.{$language->code}.description")
36-
<div class="invalid-feedback d-block">{{ $message }}</div>
37-
@enderror
38-
</div>
39-
@endforeach
40-
</div>
41-
42-
<div class="col-md-6">
43-
<div class="form-group">
44-
<label for="logo_url">{{ __('cms.brands.logo') }}</label>
45-
<div class="custom-file">
46-
<label class="btn btn-primary" for="logo_file">{{ __('cms.brands.choose_file') }}</label>
47-
<input type="file" name="logo_url" accept="image/*" class="form-control d-none" id="logo_file">
48-
</div>
49-
<div class="mt-2" id="logo_preview" style="{{ old('logo_url_preview') ? 'display:block;' : 'display:none;' }}">
50-
<img id="logo_preview_img"
51-
src="{{ old('logo_url_preview') }}"
52-
alt="selected logo"
53-
class="img-thumbnail"
54-
width="100">
55-
</div>
56-
57-
@error('logo_url')
3+
<div class="card mt-4">
4+
<div class="card-header card-header-bg text-white">
5+
<h6 class="d-flex align-items-center mb-0 dt-heading">{{ __('cms.brands.heading') }}</h6>
6+
</div>
7+
8+
<div class="card-body">
9+
<form action="{{ route('admin.brands.store') }}" method="POST" enctype="multipart/form-data" id="brandForm">
10+
@csrf
11+
12+
{{-- Hidden Base64 Preview Input --}}
13+
<input type="hidden" name="logo_preview_base64" id="logo_preview_base64" value="{{ old('logo_preview_base64') }}">
14+
15+
<div class="row">
16+
{{-- Language Tabs --}}
17+
<ul class="nav nav-tabs" id="languageTabs" role="tablist">
18+
@foreach($activeLanguages as $language)
19+
<li class="nav-item" role="presentation">
20+
<button class="nav-link {{ $loop->first ? 'active' : '' }}"
21+
id="{{ $language->name }}-tab"
22+
data-bs-toggle="tab"
23+
data-bs-target="#{{ $language->name }}"
24+
type="button"
25+
role="tab">
26+
{{ ucwords($language->name) }}
27+
</button>
28+
</li>
29+
@endforeach
30+
</ul>
31+
32+
{{-- Language Input Panels --}}
33+
<div class="tab-content mt-3" id="languageTabContent">
34+
@foreach($activeLanguages as $language)
35+
<div class="tab-pane fade show {{ $loop->first ? 'active' : '' }}" id="{{ $language->name }}" role="tabpanel">
36+
37+
<label class="form-label">{{ __('cms.brands.name') }} ({{ $language->code }})</label>
38+
<input type="text"
39+
name="translations[{{ $language->code }}][name]"
40+
class="form-control @error("translations.{$language->code}.name") is-invalid @enderror"
41+
value="{{ old("translations.{$language->code}.name") }}">
42+
@error("translations.{$language->code}.name")
5843
<div class="invalid-feedback d-block">{{ $message }}</div>
5944
@enderror
45+
46+
<label class="form-label mt-3">{{ __('cms.brands.description') }} ({{ $language->code }})</label>
47+
<textarea name="translations[{{ $language->code }}][description]"
48+
class="form-control ck-editor-multi-languages @error("translations.{$language->code}.description") is-invalid @enderror">{{ old("translations.{$language->code}.description") }}</textarea>
49+
@error("translations.{$language->code}.description")
50+
<div class="invalid-feedback d-block">{{ $message }}</div>
51+
@enderror
52+
</div>
53+
@endforeach
54+
</div>
55+
56+
{{-- Brand Logo --}}
57+
<div class="col-md-6 mt-3">
58+
<div class="form-group">
59+
<label for="logo_url">{{ __('cms.brands.logo') }}</label>
60+
61+
<div class="custom-file">
62+
<label class="btn btn-primary" for="logo_file">{{ __('cms.brands.choose_file') }}</label>
63+
<input type="file" name="logo_url" accept="image/*" class="form-control d-none" id="logo_file">
6064
</div>
65+
66+
{{-- Image Preview --}}
67+
<div class="mt-2" id="logo_preview" style="{{ old('logo_preview_base64') ? 'display:block;' : 'display:none;' }}">
68+
<img id="logo_preview_img" src="{{ old('logo_preview_base64') }}" class="img-thumbnail" width="100">
69+
</div>
70+
71+
@error('logo_url')
72+
<div class="invalid-feedback d-block">{{ $message }}</div>
73+
<small class="text-danger">Please re-upload the image.</small>
74+
@enderror
6175
</div>
6276
</div>
63-
<button type="submit" class="mt-3 btn btn-primary">{{ __('cms.brands.create') }}</button>
64-
</form>
65-
</div>
77+
</div>
78+
79+
<button type="submit" class="mt-3 btn btn-primary">{{ __('cms.brands.create') }}</button>
80+
</form>
6681
</div>
82+
</div>
6783
@endsection
6884

6985
@section('js')
86+
{{-- Show correct language tab after validation --}}
7087
<script>
7188
document.addEventListener("DOMContentLoaded", function () {
7289
@if ($errors->any())
@@ -85,32 +102,58 @@ class="img-thumbnail"
85102
@endif
86103
});
87104
</script>
105+
106+
{{-- Image Preview + Base64 Save --}}
88107
<script>
89-
document.getElementById('logo_file').addEventListener('change', function(event) {
90-
var file = event.target.files[0];
91-
var previewElement = document.getElementById('logo_preview');
92-
var previewImage = document.getElementById('logo_preview_img');
93-
94-
if (file) {
95-
var reader = new FileReader();
96-
reader.onload = function(e) {
97-
previewElement.style.display = 'block';
98-
previewImage.src = e.target.result;
99-
};
100-
reader.readAsDataURL(file);
101-
} else {
102-
previewElement.style.display = 'none';
108+
document.getElementById('logo_file').addEventListener('change', function(event) {
109+
var file = event.target.files[0];
110+
var previewElement = document.getElementById('logo_preview');
111+
var previewImage = document.getElementById('logo_preview_img');
112+
var hiddenInput = document.getElementById('logo_preview_base64');
113+
114+
if (file) {
115+
var reader = new FileReader();
116+
reader.onload = function(e) {
117+
previewElement.style.display = 'block';
118+
previewImage.src = e.target.result;
119+
hiddenInput.value = e.target.result;
120+
};
121+
reader.readAsDataURL(file);
122+
} else {
123+
previewElement.style.display = 'none';
124+
hiddenInput.value = '';
125+
}
126+
});
127+
128+
// Convert Base64 back to file before submitting
129+
document.getElementById('brandForm').addEventListener('submit', function(e) {
130+
var logoBase64 = document.getElementById('logo_preview_base64').value;
131+
if (logoBase64) {
132+
function dataURLtoBlob(dataurl) {
133+
var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
134+
bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
135+
while(n--){
136+
u8arr[n] = bstr.charCodeAt(n);
137+
}
138+
return new Blob([u8arr], {type:mime});
103139
}
104-
});
140+
141+
var fileInput = document.getElementById('logo_file');
142+
var blob = dataURLtoBlob(logoBase64);
143+
var file = new File([blob], "logo.png", {type: blob.type});
144+
145+
var dataTransfer = new DataTransfer();
146+
dataTransfer.items.add(file);
147+
fileInput.files = dataTransfer.files;
148+
}
149+
});
105150
</script>
151+
152+
{{-- CKEditor Init --}}
106153
<script src="https://cdn.ckeditor.com/ckeditor5/36.0.1/classic/ckeditor.js"></script>
107154
<script>
108-
document.querySelectorAll('.ck-editor-multi-languages').forEach((element) => {
109-
ClassicEditor
110-
.create(element)
111-
.catch(error => {
112-
console.error(error);
113-
});
114-
});
155+
document.querySelectorAll('.ck-editor-multi-languages').forEach((element) => {
156+
ClassicEditor.create(element).catch(error => console.error(error));
157+
});
115158
</script>
116159
@endsection

0 commit comments

Comments
 (0)