548 lines
27 KiB
PHP
548 lines
27 KiB
PHP
@extends('layouts.app')
|
|
@section('title', 'Gửi Thank Card')
|
|
|
|
@section('content')
|
|
@php
|
|
$tpls = [
|
|
1=>['icon'=>'💌','name'=>'Thư cảm ơn', 'thumb'=>'from-[#FFF9EC] to-[#FFF5DD]','thumbAccent'=>'#FFD066'],
|
|
2=>['icon'=>'🌸','name'=>'Sakura', 'thumb'=>'from-[#FFF5F9] to-[#FFECF4]','thumbAccent'=>'#F9A8D4'],
|
|
3=>['icon'=>'⭐','name'=>'Appreciation','thumb'=>'from-[#EFF8FF] to-[#DBEAFE]','thumbAccent'=>'#93C5FD'],
|
|
4=>['icon'=>'🎉','name'=>'Celebration', 'thumb'=>'from-[#F5F0FF] to-[#EDE9FE]','thumbAccent'=>'#C4B5FD'],
|
|
];
|
|
@endphp
|
|
|
|
<div class="max-w-[760px] mx-auto w-full pb-12 px-4 lg:px-0">
|
|
|
|
{{-- Header --}}
|
|
<div class="relative bg-gradient-to-r from-[#eff4ff] to-[#faeffa] rounded-[24px] p-7 md:p-10 mb-7 overflow-hidden border border-white shadow-[0_10px_30px_rgba(15,23,42,0.05)]">
|
|
<div class="absolute -top-10 -right-10 w-44 h-44 bg-white/25 rounded-full blur-2xl"></div>
|
|
<div class="absolute -bottom-8 -left-8 w-32 h-32 bg-white/20 rounded-full blur-xl"></div>
|
|
<div class="relative z-10 flex items-center gap-4">
|
|
<div class="w-14 h-14 rounded-2xl bg-white/80 shadow-sm flex items-center justify-center text-3xl shrink-0">💌</div>
|
|
<div>
|
|
<h1 class="text-[26px] md:text-[30px] font-extrabold text-[#1a2b49] tracking-tight leading-tight">Gửi Thank Card</h1>
|
|
<p class="text-[12px] text-gray-500 font-medium mt-1 w-full md:whitespace-nowrap leading-relaxed">Gửi lời cảm ơn chân thành đến đồng nghiệp và lan tỏa văn hóa ghi nhận trong tổ chức.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<form id="sendCardForm">
|
|
@csrf
|
|
<input type="hidden" name="template_id" id="template_id" value="1">
|
|
|
|
{{-- Template Gallery --}}
|
|
<div class="bg-white rounded-[20px] shadow-[0_4px_20px_rgba(15,23,42,0.06)] border border-[#f0f4fa] p-6 mb-5">
|
|
<p class="text-[13px] font-bold text-gray-400 uppercase tracking-widest mb-4">Chọn mẫu thiệp <span class="text-[#3462f7]">*</span></p>
|
|
<div class="grid grid-cols-2 sm:grid-cols-4 gap-3">
|
|
@foreach($tpls as $id => $t)
|
|
<button type="button" onclick="selectTpl(this,{{$id}})"
|
|
data-tpl="{{$id}}"
|
|
class="tpl-btn relative group flex flex-col items-center gap-2 p-4 rounded-2xl border-2 transition-all duration-200 bg-gradient-to-br {{$t['thumb']}} cursor-pointer
|
|
{{$loop->first ? 'border-[#3462f7] ring-2 ring-[#3462f7]/20 shadow-md' : 'border-[#e8edf4] hover:border-[#3462f7]/40 hover:shadow-md'}}">
|
|
<span class="tpl-check absolute top-2 right-2 w-5 h-5 rounded-full bg-[#3462f7] flex items-center justify-center {{$loop->first ? 'opacity-100':'opacity-0'}} transition-opacity duration-200">
|
|
<svg class="w-3 h-3 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="3"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/></svg>
|
|
</span>
|
|
<span class="text-3xl group-hover:scale-110 transition-transform duration-200 select-none">{{$t['icon']}}</span>
|
|
<span class="text-[12px] font-bold text-[#1a2b49] text-center leading-tight">{{$t['name']}}</span>
|
|
</button>
|
|
@endforeach
|
|
</div>
|
|
</div>
|
|
|
|
{{-- e-Card (form inside the card) --}}
|
|
<div id="eCard" class="relative rounded-[24px] overflow-hidden shadow-[0_20px_60px_rgba(15,23,42,0.12)] border-2 mb-5 transition-all duration-500"
|
|
style="background:linear-gradient(145deg,#FFF9EC,#FFFDF7);border-color:#FFE4A0;">
|
|
|
|
{{-- Decorative SVG top --}}
|
|
<div id="cardDeco" class="relative overflow-hidden" style="height:140px;background:linear-gradient(135deg,#FFF5CC,#FFE8A0);">
|
|
{{-- blob 1 --}}
|
|
<div class="absolute -top-8 -left-8 w-36 h-36 rounded-full opacity-40 blur-2xl" id="blob1" style="background:#FFD066;"></div>
|
|
<div class="absolute -bottom-6 -right-6 w-28 h-28 rounded-full opacity-30 blur-xl" id="blob2" style="background:#FFAD00;"></div>
|
|
{{-- SVG decoration --}}
|
|
<div id="decoSvg" class="absolute inset-0 flex items-center justify-center select-none pointer-events-none">
|
|
{{-- Template 1 default: envelope --}}
|
|
<div class="flex flex-col items-center gap-1 z-10">
|
|
<span id="cardBigIcon" class="text-[72px] leading-none drop-shadow-lg" style="filter:drop-shadow(0 4px 16px rgba(0,0,0,0.10))">💌</span>
|
|
<span id="cardLabel" class="text-[13px] font-bold uppercase tracking-[0.2em] mt-1" style="color:#B8860B;">Thank Card</span>
|
|
</div>
|
|
</div>
|
|
{{-- wavy bottom --}}
|
|
<svg class="absolute bottom-0 left-0 w-full" viewBox="0 0 760 30" preserveAspectRatio="none" style="height:30px;">
|
|
<path id="wavePath" d="M0,20 Q190,0 380,20 Q570,40 760,20 L760,30 L0,30 Z" fill="white" opacity="0.8"/>
|
|
</svg>
|
|
</div>
|
|
|
|
{{-- Card body: form fields ON the card --}}
|
|
<div class="px-8 py-7 bg-white/0">
|
|
|
|
{{-- To: Người nhận --}}
|
|
<div class="mb-6">
|
|
<label class="block text-[11px] font-bold uppercase tracking-widest mb-2" id="labelTo" style="color:#B8860B;">Gửi đến</label>
|
|
<select name="receiver" id="receiver" class="form-input rounded-xl border-[#e8edf4] bg-white/90" required>
|
|
<option value="" disabled selected></option>
|
|
@foreach($users as $u)
|
|
@php $dept=$u->department_name??'Team'; @endphp
|
|
<option value="{{$u->msnv}}">{{$u->name}} - {{$u->msnv}} ({{$u->mail}}) - {{$dept}}</option>
|
|
@endforeach
|
|
</select>
|
|
<span id="error-receiver" class="text-red-500 text-[12px] font-semibold mt-1.5 hidden block"></span>
|
|
</div>
|
|
|
|
{{-- Số lượng --}}
|
|
<div class="mb-6">
|
|
<label class="block text-[11px] font-bold uppercase tracking-widest mb-2" id="labelQty" style="color:#B8860B;">Số lượng thẻ</label>
|
|
<div class="flex items-center gap-3">
|
|
<select name="amount" id="amount" class="form-input rounded-xl border-[#e8edf4] bg-white/90 !w-auto !px-5" required>
|
|
<option value="1">1 Thẻ</option>
|
|
<option value="2">2 Thẻ</option>
|
|
<option value="3">3 Thẻ</option>
|
|
<option value="4">4 Thẻ</option>
|
|
<option value="{{App\Models\Administration::MAX_SEND_CARD_PER_MONTH}}">{{App\Models\Administration::MAX_SEND_CARD_PER_MONTH}} Thẻ (Tối đa)</option>
|
|
</select>
|
|
<span class="text-[12px] text-gray-400 font-medium">/ tháng</span>
|
|
</div>
|
|
<span id="error-amount" class="text-red-500 text-[12px] font-semibold mt-1.5 hidden block"></span>
|
|
</div>
|
|
|
|
{{-- Confirm checkbox --}}
|
|
<div class="mb-2">
|
|
<label class="flex items-start gap-3 p-4 rounded-2xl border border-[#e8edf4] bg-white/70 hover:bg-white/90 transition-colors cursor-pointer">
|
|
<input type="checkbox" name="confirm_written" id="confirm_written" value="1"
|
|
class="w-4 h-4 mt-0.5 text-[#3462f7] border-[#d9dfe7] rounded focus:ring-[#3462f7]/20 cursor-pointer shrink-0" required>
|
|
<span class="text-[13px] font-medium text-gray-600 leading-relaxed select-none">
|
|
Tôi xác nhận đã viết nội dung trên ThankCard và sẵn sàng gửi đến người nhận. <span class="text-[#3462f7] font-bold">*</span>
|
|
</span>
|
|
</label>
|
|
<span id="error-confirm_written" class="text-red-500 text-[12px] font-semibold mt-1.5 hidden block"></span>
|
|
</div>
|
|
|
|
{{-- Card footer line --}}
|
|
<div class="mt-6 pt-4 border-t border-dashed" id="cardFooterLine" style="border-color:#FFE4A0;">
|
|
<div class="flex items-center justify-between">
|
|
<span class="text-[11px] text-gray-400 font-medium">Từ: <span class="font-bold text-gray-500">{{ Auth::user()->name ?? 'Bạn' }}</span></span>
|
|
<span id="cardFooterBadge" class="text-[10px] font-bold px-2.5 py-1 rounded-full" style="background:rgba(255,176,0,0.15);color:#B8860B;">Thư cảm ơn</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Info note with dynamic accent -->
|
|
<div id="infoNote" class="flex items-start gap-3 bg-gradient-to-r from-[#eff4ff] to-[#f5f0ff] p-4 rounded-2xl border mb-5" style="border-color:#dce7ff;">
|
|
<div class="w-8 h-8 rounded-xl bg-white flex items-center justify-center shrink-0 shadow-sm">
|
|
<svg id="noteIcon" class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
|
|
</div>
|
|
<div>
|
|
<p id="noteHeader" class="text-[12px] font-bold mb-0.5" style="color:#3462f7;">Lưu ý</p>
|
|
<p class="text-[13px] text-gray-600 font-medium leading-relaxed">Chỉ được gửi tối đa <strong>{{ App\Models\Administration::MAX_SEND_CARD_PER_MONTH }} thẻ</strong> cho cùng một người trong tháng để đảm bảo tính công bằng.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- General Error notification block -->
|
|
<div id="formGeneralError" class="p-4 bg-red-50 border border-red-100 text-red-800 rounded-2xl text-xs font-semibold leading-relaxed flex items-start gap-2 shadow-sm mb-5 whitespace-pre-line hidden">
|
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-5 h-5 shrink-0 text-red-600">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
|
</svg>
|
|
<span id="generalErrorMessage"></span>
|
|
</div>
|
|
|
|
{{-- Actions --}}
|
|
<div class="flex flex-col sm:flex-row justify-end gap-3">
|
|
<a href="{{ route('user.dashboard') }}" id="btnCancelLink" class="btn-secondary rounded-xl font-bold flex items-center justify-center gap-2 order-2 sm:order-1">
|
|
<svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M10 19l-7-7m0 0l7-7m-7 7h18"/></svg>Hủy bỏ
|
|
</a>
|
|
<button type="button" id="btnSubmitSend" class="btn-primary rounded-xl font-bold hover:!bg-blue-700 shadow-md shadow-blue-500/15 flex items-center justify-center gap-2 order-1 sm:order-2">
|
|
<svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M6 12L3.269 3.126A59.768 59.768 0 0121.485 12 59.77 59.77 0 013.27 20.876L5.999 12zm0 0h7.5"/></svg>Gửi Card
|
|
</button>
|
|
</div>
|
|
|
|
</form>
|
|
</div>
|
|
|
|
<x-modal id="customModal" title="Thông báo" maxWidth="md">
|
|
<p id="modalMessage" class="text-sm text-gray-600 leading-relaxed"></p>
|
|
<x-slot name="footer">
|
|
<div id="modalActions" class="w-full flex flex-col sm:flex-row-reverse gap-3"></div>
|
|
</x-slot>
|
|
</x-modal>
|
|
@endsection
|
|
|
|
@push('scripts')
|
|
<script src="{{ asset('js/jquery.min.js') }}"></script>
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
|
|
// ── Theme data ───────────────────────────────────────────────────
|
|
const T = {
|
|
1:{icon:'💌',name:'Thư cảm ơn', label:'THANK CARD',
|
|
deco:'linear-gradient(135deg,#FFF5CC,#FFE8A0)',
|
|
card:'linear-gradient(145deg,#FFF9EC,#FFFDF7)',
|
|
border:'#FFE4A0', blob1:'#FFD066', blob2:'#FFAD00',
|
|
accent:'#B8860B', badgeBg:'rgba(255,176,0,0.15)', wave:'white',
|
|
footerBorder:'#FFE4A0'},
|
|
2:{icon:'🌸',name:'Sakura', label:'SAKURA CARD',
|
|
deco:'linear-gradient(135deg,#FFE8F4,#FFD6EC)',
|
|
card:'linear-gradient(145deg,#FFF8FB,#FFF3F8)',
|
|
border:'#FFBCD9', blob1:'#F9A8D4', blob2:'#F472B6',
|
|
accent:'#be185d', badgeBg:'rgba(244,114,182,0.12)', wave:'white',
|
|
footerBorder:'#FFBCD9'},
|
|
3:{icon:'⭐',name:'Appreciation',label:'APPRECIATION',
|
|
deco:'linear-gradient(135deg,#DBEAFE,#BFDBFE)',
|
|
card:'linear-gradient(145deg,#F0F8FF,#EAF3FF)',
|
|
border:'#BFDBFE', blob1:'#93C5FD', blob2:'#60A5FA',
|
|
accent:'#1d4ed8', badgeBg:'rgba(96,165,250,0.12)', wave:'white',
|
|
footerBorder:'#BFDBFE'},
|
|
4:{icon:'🎉',name:'Celebration', label:'CELEBRATION',
|
|
deco:'linear-gradient(135deg,#EDE9FE,#DDD6FE)',
|
|
card:'linear-gradient(145deg,#F8F5FF,#F3EFFF)',
|
|
border:'#DDD6FE', blob1:'#C4B5FD', blob2:'#A78BFA',
|
|
accent:'#6d28d9', badgeBg:'rgba(167,139,250,0.12)', wave:'white',
|
|
footerBorder:'#DDD6FE'},
|
|
};
|
|
|
|
// ── Theme slugs ──────────────────────────────────────────────────
|
|
const templateSlugs = {
|
|
1: 'thu_cam_on',
|
|
2: 'sakura',
|
|
3: 'appreciation',
|
|
4: 'celebration'
|
|
};
|
|
const templateIds = {
|
|
'thu_cam_on': 1,
|
|
'sakura': 2,
|
|
'appreciation': 3,
|
|
'celebration': 4
|
|
};
|
|
|
|
// Current selected template ID (defaults to 1) // read from hidden input or localStorage
|
|
const savedTemplateSlug = localStorage.getItem('thankcard_selected_template');
|
|
let currentTpl = (savedTemplateSlug && templateIds[savedTemplateSlug])
|
|
|| parseInt(localStorage.getItem('selectedTemplate'))
|
|
|| parseInt(document.getElementById('template_id').value)
|
|
|| 1;
|
|
|
|
function applyTheme(id) {
|
|
const t = T[id]; if (!t) return;
|
|
const $card = document.getElementById('eCard');
|
|
const $deco = document.getElementById('cardDeco');
|
|
const $b1 = document.getElementById('blob1');
|
|
const $b2 = document.getElementById('blob2');
|
|
const $bigIcon = document.getElementById('cardBigIcon');
|
|
const $label = document.getElementById('cardLabel');
|
|
const $wave = document.getElementById('wavePath');
|
|
const $lTo = document.getElementById('labelTo');
|
|
const $lQty = document.getElementById('labelQty');
|
|
const $badge = document.getElementById('cardFooterBadge');
|
|
const $footerL = document.getElementById('cardFooterLine');
|
|
|
|
$card.style.background = t.card;
|
|
$card.style.borderColor = t.border;
|
|
$deco.style.background = t.deco;
|
|
$b1.style.background = t.blob1;
|
|
$b2.style.background = t.blob2;
|
|
$bigIcon.textContent = t.icon;
|
|
$label.textContent = t.label;
|
|
$label.style.color = t.accent;
|
|
$wave.setAttribute('fill', t.wave);
|
|
$lTo.style.color = t.accent;
|
|
$lQty.style.color = t.accent;
|
|
$badge.textContent = t.name;
|
|
$badge.style.background = t.badgeBg;
|
|
$badge.style.color = t.accent;
|
|
$footerL.style.borderColor = t.footerBorder;
|
|
// sync primary submit button color
|
|
const $submit = document.getElementById('btnSubmitSend');
|
|
if ($submit) {
|
|
$submit.style.background = t.accent;
|
|
$submit.style.borderColor = t.accent;
|
|
}
|
|
// sync info note colors
|
|
const $info = document.getElementById('infoNote');
|
|
if ($info) {
|
|
$info.style.borderColor = t.accent;
|
|
}
|
|
const $noteIcon = document.getElementById('noteIcon');
|
|
if ($noteIcon) {
|
|
$noteIcon.style.stroke = t.accent;
|
|
$noteIcon.style.color = t.accent;
|
|
}
|
|
const $noteHeader = document.getElementById('noteHeader');
|
|
if ($noteHeader) {
|
|
$noteHeader.style.color = t.accent;
|
|
}
|
|
// store current template for modal sync
|
|
currentTpl = id;
|
|
}
|
|
|
|
// ── Template picker ──────────────────────────────────────────────
|
|
window.selectTpl = function(el, id, e) {
|
|
if (e) e.preventDefault();
|
|
document.querySelectorAll('.tpl-btn').forEach(b => {
|
|
b.classList.remove('border-[#3462f7]','ring-2','ring-[#3462f7]/20','shadow-md');
|
|
b.classList.add('border-[#e8edf4]');
|
|
b.querySelector('.tpl-check').classList.replace('opacity-100','opacity-0');
|
|
});
|
|
el.classList.remove('border-[#e8edf4]');
|
|
el.classList.add('border-[#3462f7]','ring-2','ring-[#3462f7]/20','shadow-md');
|
|
el.querySelector('.tpl-check').classList.replace('opacity-0','opacity-100');
|
|
document.getElementById('template_id').value = id;
|
|
// persist selection
|
|
localStorage.setItem('selectedTemplate', id);
|
|
if (templateSlugs[id]) {
|
|
localStorage.setItem('thankcard_selected_template', templateSlugs[id]);
|
|
}
|
|
applyTheme(id);
|
|
};
|
|
|
|
// Restore saved selections on load
|
|
const savedReceiver = localStorage.getItem('selectedReceiver');
|
|
if (savedReceiver) document.getElementById('receiver').value = savedReceiver;
|
|
const savedAmount = localStorage.getItem('selectedAmount');
|
|
if (savedAmount) document.getElementById('amount').value = savedAmount;
|
|
|
|
// Listen for changes to persist them
|
|
document.getElementById('receiver').addEventListener('change', function(){
|
|
localStorage.setItem('selectedReceiver', this.value);
|
|
});
|
|
document.getElementById('amount').addEventListener('change', function(){
|
|
localStorage.setItem('selectedAmount', this.value);
|
|
});
|
|
|
|
// Apply saved template on initial load
|
|
applyTheme(currentTpl);
|
|
document.getElementById('template_id').value = currentTpl;
|
|
// Sync template button UI
|
|
document.querySelectorAll('.tpl-btn').forEach(b => {
|
|
const id = parseInt(b.getAttribute('data-tpl'));
|
|
if (id === currentTpl) {
|
|
b.classList.remove('border-[#e8edf4]');
|
|
b.classList.add('border-[#3462f7]','ring-2','ring-[#3462f7]/20','shadow-md');
|
|
const check = b.querySelector('.tpl-check');
|
|
if (check) {
|
|
check.classList.remove('opacity-0');
|
|
check.classList.add('opacity-100');
|
|
}
|
|
} else {
|
|
b.classList.remove('border-[#3462f7]','ring-2','ring-[#3462f7]/20','shadow-md');
|
|
b.classList.add('border-[#e8edf4]');
|
|
const check = b.querySelector('.tpl-check');
|
|
if (check) {
|
|
check.classList.remove('opacity-100');
|
|
check.classList.add('opacity-0');
|
|
}
|
|
}
|
|
});
|
|
|
|
// ── TomSelect ────────────────────────────────────────────────────
|
|
const receiverSelect = new TomSelect('#receiver', {
|
|
create:false, searchField:['text','value'], maxOptions:10,
|
|
sortField:{field:'text',direction:'asc'},
|
|
placeholder:'Nhập tên hoặc mã nhân viên...',
|
|
render:{
|
|
option:function(d,e){
|
|
const p=d.text.split(' - '),n=p[0]||'',i=p.slice(1).join(' · ')||'';
|
|
return`<div class="px-3 py-2.5 flex flex-col gap-0.5 hover:bg-[#eff4ff] cursor-pointer transition-colors">
|
|
<span class="font-bold text-[14px] text-[#1a2b49]">${e(n)}</span>
|
|
<span class="text-[11px] text-gray-400">${e(i)}</span></div>`;
|
|
},
|
|
item:function(d,e){
|
|
const n=d.text.split(' - ')[0]||'';
|
|
return`<div class="font-semibold text-[14px] text-[#1a2b49]">${e(n)} <span class="text-gray-400 font-normal text-[12px]">(${e(d.value)})</span></div>`;
|
|
},
|
|
no_results:()=>'<div class="p-3 text-[13px] text-gray-400 text-center">Không tìm thấy nhân viên nào</div>'
|
|
}
|
|
});
|
|
|
|
// ── Modal ────────────────────────────────────────────────────────
|
|
const $mTitle = document.getElementById('modal-title-customModal');
|
|
const $mMsg = document.getElementById('modalMessage');
|
|
const $mAct = document.getElementById('modalActions');
|
|
|
|
const showModal = (title, msg, confirm, cb) => {
|
|
$mTitle.textContent = title;
|
|
$mMsg.innerHTML = msg.replace(/\n/g,'<br>');
|
|
if (confirm) {
|
|
$mTitle.className = 'text-lg font-bold';
|
|
// Sync text color with current template's accent color
|
|
$mTitle.style.color = T[currentTpl].accent;
|
|
// Use accent color of current template for confirm button background
|
|
const accent = T[currentTpl].accent;
|
|
$mAct.innerHTML = `<button id="btnOk" class="w-full sm:w-auto btn-primary !px-6 rounded-xl font-bold" style="background:${accent};border-color:${accent};">Xác nhận gửi</button>
|
|
<button id="btnCancel" class="w-full sm:w-auto btn-secondary !px-6 rounded-xl font-bold">Hủy</button>`;
|
|
document.getElementById('btnCancel').onclick = () => window.closeModal('customModal');
|
|
document.getElementById('btnOk').addEventListener('click', function(){
|
|
this.disabled=true;
|
|
this.innerHTML='<span class="flex items-center gap-2"><svg class="animate-spin h-4 w-4 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg> Đang xử lý...</span>';
|
|
cb();
|
|
});
|
|
} else {
|
|
$mTitle.className = 'text-lg font-bold text-[#1a2b49]';
|
|
$mTitle.style.color = ''; // reset style
|
|
$mAct.innerHTML = `<button id="btnClose" class="w-full sm:w-auto btn-secondary !px-6 rounded-xl font-bold">OK</button>`;
|
|
document.getElementById('btnClose').onclick = () => window.closeModal('customModal');
|
|
}
|
|
window.openModal('customModal');
|
|
};
|
|
|
|
// ── Error Helpers ────────────────────────────────────────────────
|
|
function clearErrors() {
|
|
document.getElementById('formGeneralError').classList.add('hidden');
|
|
document.getElementById('generalErrorMessage').textContent = '';
|
|
|
|
document.querySelectorAll('[id^="error-"]').forEach(el => {
|
|
el.textContent = '';
|
|
el.classList.add('hidden');
|
|
});
|
|
}
|
|
|
|
function validateForm() {
|
|
clearErrors();
|
|
let isValid = true;
|
|
|
|
const receiver = document.getElementById('receiver').value;
|
|
if (!receiver) {
|
|
const errEl = document.getElementById('error-receiver');
|
|
if (errEl) {
|
|
errEl.textContent = 'Vui lòng chọn người nhận.';
|
|
errEl.classList.remove('hidden');
|
|
}
|
|
isValid = false;
|
|
}
|
|
|
|
const amount = document.getElementById('amount').value;
|
|
if (!amount) {
|
|
const errEl = document.getElementById('error-amount');
|
|
if (errEl) {
|
|
errEl.textContent = 'Vui lòng chọn số lượng thẻ.';
|
|
errEl.classList.remove('hidden');
|
|
}
|
|
isValid = false;
|
|
}
|
|
|
|
const confirm = document.getElementById('confirm_written').checked;
|
|
if (!confirm) {
|
|
const errEl = document.getElementById('error-confirm_written');
|
|
if (errEl) {
|
|
errEl.textContent = 'Bạn cần xác nhận đã viết nội dung trên ThankCard.';
|
|
errEl.classList.remove('hidden');
|
|
}
|
|
isValid = false;
|
|
}
|
|
|
|
return isValid;
|
|
}
|
|
|
|
// ── Submit ───────────────────────────────────────────────────────
|
|
const $submitBtn = document.getElementById('btnSubmitSend');
|
|
const btnOriginalHtml = $submitBtn.innerHTML;
|
|
|
|
function setButtonLoading(loading) {
|
|
if (loading) {
|
|
$submitBtn.disabled = true;
|
|
$submitBtn.innerHTML = `<svg class="animate-spin h-4 w-4 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg><span>⏳ Đang gửi...</span>`;
|
|
} else {
|
|
$submitBtn.disabled = false;
|
|
$submitBtn.innerHTML = btnOriginalHtml;
|
|
// Keep selected template button accent style synchronized
|
|
const accent = T[currentTpl]?.accent;
|
|
if (accent) {
|
|
$submitBtn.style.background = accent;
|
|
$submitBtn.style.borderColor = accent;
|
|
}
|
|
}
|
|
}
|
|
|
|
// User state for client-side validation
|
|
let userCard = {{ (int) auth()->user()->card }};
|
|
let userFlagSend = {{ (int) auth()->user()->flag_send }};
|
|
|
|
$submitBtn.addEventListener('click', function(){
|
|
if (!validateForm()) { return; }
|
|
|
|
const msnv = document.getElementById('receiver').value;
|
|
const amount = parseInt(document.getElementById('amount').value, 10);
|
|
|
|
if (userCard < amount || userFlagSend === 0) {
|
|
showModal('Thông báo',
|
|
"Bạn không thể gửi Thank Card.\nCó thể bạn đã sử dụng hết số lượng Thank Card hoặc chưa được cấp quyền gửi. Vui lòng liên hệ Admin để được hỗ trợ.",
|
|
false);
|
|
return;
|
|
}
|
|
|
|
showModal('Xác nhận gửi Thank Card',
|
|
'Đừng quên hoàn thành Thank Card của bạn nhé!\n Chúng mình sẽ gửi thông báo đến người nhận\nđể họ đến khu vực nhận thư.',
|
|
true, () => {
|
|
window.closeModal('customModal');
|
|
setButtonLoading(true);
|
|
clearErrors();
|
|
|
|
$.ajax({
|
|
url:'{{ route("user.store_send") }}', method:'POST', contentType:'application/json',
|
|
headers:{'X-CSRF-TOKEN':$('meta[name="csrf-token"]').attr('content')},
|
|
data:JSON.stringify({receiver:msnv,amount:amount}),
|
|
success:function(res){
|
|
if(res.success){
|
|
setButtonLoading(false);
|
|
|
|
// Reset form fields
|
|
receiverSelect.clear();
|
|
document.getElementById('amount').value = '1';
|
|
document.getElementById('confirm_written').checked = false;
|
|
|
|
// Update local JS state
|
|
userCard -= amount;
|
|
if (userCard <= 0) {
|
|
userCard = 0;
|
|
userFlagSend = 0;
|
|
}
|
|
|
|
// Clear local storage for inputs (but not template)
|
|
localStorage.removeItem('selectedReceiver');
|
|
localStorage.removeItem('selectedAmount');
|
|
|
|
// Display toast message
|
|
if (window.showToast) {
|
|
window.showToast("🎉 ThankCard đã được gửi thành công!", "success");
|
|
} else {
|
|
alert("🎉 ThankCard đã được gửi thành công!");
|
|
}
|
|
} else {
|
|
setButtonLoading(false);
|
|
if (res.errors) {
|
|
for (const [field, messages] of Object.entries(res.errors)) {
|
|
const errEl = document.getElementById('error-' + field);
|
|
if (errEl) {
|
|
errEl.textContent = messages.join(' ');
|
|
errEl.classList.remove('hidden');
|
|
}
|
|
}
|
|
}
|
|
if (res.message) {
|
|
document.getElementById('generalErrorMessage').textContent = res.message;
|
|
document.getElementById('formGeneralError').classList.remove('hidden');
|
|
}
|
|
}
|
|
},
|
|
error:function(xhr){
|
|
setButtonLoading(false);
|
|
const res = xhr.responseJSON;
|
|
if (res && res.errors) {
|
|
for (const [field, messages] of Object.entries(res.errors)) {
|
|
const errEl = document.getElementById('error-' + field);
|
|
if (errEl) {
|
|
errEl.textContent = messages.join(' ');
|
|
errEl.classList.remove('hidden');
|
|
}
|
|
}
|
|
}
|
|
const errMsg = res?.message || 'Không thể kết nối đến máy chủ.';
|
|
document.getElementById('generalErrorMessage').textContent = errMsg;
|
|
document.getElementById('formGeneralError').classList.remove('hidden');
|
|
}
|
|
});
|
|
}
|
|
);
|
|
});
|
|
});
|
|
</script>
|
|
@endpush
|