Initial commit for new repo
All checks were successful
Scan for leaked secrets using Kingfisher / kingfisher-secrets-scan (push) Successful in 1m4s

This commit is contained in:
dhanabalan
2025-12-16 17:05:04 +05:30
commit 3f0d529640
862 changed files with 141157 additions and 0 deletions

3
resources/css/app.css Normal file
View File

@@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

1
resources/js/app.js Normal file
View File

@@ -0,0 +1 @@
import './bootstrap';

4
resources/js/bootstrap.js vendored Normal file
View File

@@ -0,0 +1,4 @@
import axios from 'axios';
window.axios = axios;
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';

View File

@@ -0,0 +1,122 @@
import ChartDataLabels from 'chartjs-plugin-datalabels';
import Chart from 'chart.js/auto';
Chart.register(ChartDataLabels);
window.Chart = Chart;
window.filamentChartJsPlugins ??= [];
if (!window.filamentChartJsPlugins.includes(ChartDataLabels)) {
window.filamentChartJsPlugins.push(ChartDataLabels);
}
// window.filamentChartJsPlugins.push(ChartDataLabels);
// Chart.defaults.plugins.datalabels = {
// anchor: 'end',
// align: 'right',
// color: '#000',
// font: {
// weight: 'bold',
// size: 11,
// },
// formatter: (value) => `${value} min`,
// backgroundColor: function (context) {
// const value = context.dataset.data[context.dataIndex];
// return Number(value) !== 0 ? 'rgba(40, 167, 69, 0.1)' : 'transparent';
// },
// };
// Set global datalabel options
// ChartDataLabels.defaults.anchor = 'end'; // or 'start', 'center', etc.
// ChartDataLabels.defaults.align = 'right'; // or 'left', 'top', 'bottom', etc.
// // // Conditionally set backgroundColor
// ChartDataLabels.defaults.backgroundColor = function(context) {
// const value = context.dataset.data[context.dataIndex];
// return Number(value) !== 0 ? 'rgba(40, 167, 69, 0.1)' : 'transparent';
// };
// // // Conditionally set borderRadius
// ChartDataLabels.defaults.borderRadius = function(context) {
// const value = context.dataset.data[context.dataIndex];
// return Number(value) !== 0 ? 8 : 0;
// };
// Conditionally set padding
// ChartDataLabels.defaults.padding = function(context) {
// const value = context.dataset.data[context.dataIndex];
// return Number(value) !== 0 ? 6 : 0;
// };
// // // Conditionally set color
// ChartDataLabels.defaults.color = function(context) {
// const value = context.dataset.data[context.dataIndex];
// return Number(value) !== 0 ? '#28a745' : 'transparent';
// };
// // Conditionally set font
// ChartDataLabels.defaults.font = function(context) {
// const value = context.dataset.data[context.dataIndex];
// return Number(value) !== 0 ? { weight: 'bold', size: 14 } : { size: 0 };
// };
// ChartDataLabels.defaults.formatter = function(value, context) {
// if (Number(value) === 0){
// return '';
// }
// return 'Count: ' + value;
// };
// const ctx = document.getElementById('hourly-production');
// if (ctx && window.hourlyProductionData && window.hourlyProductionData.labels.length)
// {
// new Chart(ctx.getContext('2d'), {
// type: 'line',
// data: window.hourlyProductionData,
// options: {
// plugins: {
// datalabels: {
// anchor: 'end',
// align: 'right',
// backgroundColor: function(context) {
// const value = context.dataset.data[context.dataIndex];
// return Number(value) !== 0 ? 'rgba(40, 167, 69, 0.1)' : 'transparent';
// },
// borderRadius: function(context) {
// const value = context.dataset.data[context.dataIndex];
// return Number(value) !== 0 ? 8 : 0;
// },
// padding: function(context) {
// const value = context.dataset.data[context.dataIndex];
// return Number(value) !== 0 ? 6 : 0;
// },
// color: function(context) {
// const value = context.dataset.data[context.dataIndex];
// return Number(value) !== 0 ? '#28a745' : 'transparent';
// },
// font: function(context) {
// const value = context.dataset.data[context.dataIndex];
// return Number(value) !== 0 ? { weight: 'bold', size: 14 } : { size: 0 };
// },
// formatter: function(value, context) {
// if (Number(value) === 0){
// return '';
// }
// return 'Count: ' + value;
// }
// }
// }
// },
// plugins: [ChartDataLabels]
// });
// }
// else {
// console.log('No data available for the chart.');
// }

View File

@@ -0,0 +1,446 @@
{{-- <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Forgot Password</title>
</head>
<body>
<h1>Forgot Password</h1>
@if (session('status'))
<div style="color: green;">
{{ session('status') }}
</div>
@endif
<form method="POST" action="{{ route('filament.admin.forgot-password.email') }}">
@csrf
<label>Email:</label>
<input type="email" name="email" required>
<button type="submit">Send Password Reset Link</button>
</form>
<a href="{{ \Filament\Facades\Filament::getPanel('admin')->getLoginUrl() }}">Back to login</a>
</body>
</html> --}}
{{-- <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Forgot Password</title>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="bg-gray-50 flex items-center justify-center h-screen">
<div class="w-full max-w-md bg-white p-8 rounded-2xl shadow-lg">
<div class="text-center mb-6">
<img src="{{ asset('/assets/crilogo1.png') }}" alt="Logo" class="mx-auto w-24 h-24">
<h1 class="mt-4 text-2xl font-bold text-gray-700">Forgot Password</h1>
<p class="text-gray-500 mt-1 text-sm">Enter your email to reset your password</p>
</div>
@if (session('status'))
<div class="mb-4 text-green-600 text-sm text-center">
{{ session('status') }}
</div>
@endif
@if ($errors->any())
<div class="mb-4 text-red-600 text-sm text-center">
{{ $errors->first() }}
</div>
@endif
<form method="POST" action="{{ route('filament.admin.forgot-password.email') }}">
@csrf
<div class="mb-4">
<label class="block text-gray-700 text-sm font-medium mb-2">Email</label>
<input type="email" name="email" required
class="w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-amber-500 focus:border-amber-500">
</div>
<button type="submit"
class="w-full py-2 px-4 bg-amber-500 text-white font-semibold rounded-lg shadow-md hover:bg-amber-600 transition duration-200">
Generate OTP
</button>
</form>
<div class="mt-6 text-center">
<a href="{{ \Filament\Facades\Filament::getPanel('admin')->getLoginUrl() }}"
class="text-amber-500 hover:underline font-medium text-sm">
Back to login
</a>
</div>
</div>
</body>
</html> --}}
{{-- Imp --}}
{{-- <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Forgot Password</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js" defer></script>
</head>
<body class="bg-gray-50 flex items-center justify-center h-screen">
<div class="w-full max-w-md bg-white p-8 rounded-2xl shadow-lg" x-data="{ otpSent: false, email: '', error: '' }">
<div class="text-center mb-6">
<img src="{{ asset('/assets/crilogo1.png') }}" alt="Logo" class="mx-auto w-24 h-24">
<h1 class="mt-4 text-2xl font-bold text-gray-700">Forgot Password</h1>
<p class="text-gray-500 mt-1 text-sm">Enter your email to reset your password</p>
</div>
<!-- Display session status -->
@if (session('status'))
<div class="mb-4 text-green-600 text-sm text-center">
{{ session('status') }}
</div>
@endif
<!-- Display backend validation errors -->
@if ($errors->any())
<div class="mb-4 text-red-600 text-sm text-center">
{{ $errors->first() }}
</div>
@endif
<!-- Front-end error -->
<div class="mb-4 text-red-600 text-sm text-center" x-show="error" x-text="error"></div>
<!-- Frontend success -->
<div class="mb-4 text-green-600 text-sm text-center" x-show="success" x-text="success"></div>
<form method="POST" action="{{ route('filament.admin.forgot-password.otp') }}">
@csrf
<!-- Email input -->
<div class="mb-4">
<label class="block text-gray-700 text-sm font-medium mb-2">Email</label>
<input type="email" name="email" x-model="email" required
class="w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-amber-500 focus:border-amber-500"
x-bind:readonly="otpSent">
</div>
<!-- OTP input -->
<div class="mb-4" x-show="otpSent" x-transition>
<label class="block text-gray-700 text-sm font-medium mb-2">Enter OTP</label>
<input type="text" name="otp" required
class="w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-amber-500 focus:border-amber-500">
</div>
<!-- New password -->
<div class="mb-4" x-show="otpSent" x-transition>
<label class="block text-gray-700 text-sm font-medium mb-2">New Password</label>
<input type="password" name="password" required
class="w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-amber-500 focus:border-amber-500">
</div>
<!-- Confirm password -->
<div class="mb-4" x-show="otpSent" x-transition>
<label class="block text-gray-700 text-sm font-medium mb-2">Confirm Password</label>
<input type="password" name="password_confirmation" required
class="w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-amber-500 focus:border-amber-500">
</div>
<!-- Submit / Generate OTP button -->
<button type="submit"
class="w-full py-2 px-4 bg-amber-500 text-white font-semibold rounded-lg shadow-md hover:bg-amber-600 transition duration-200"
x-on:click.prevent="
error = '';
if (!email) {
error = 'Please enter your email';
return;
}
otpSent = true;
">
<span x-text="otpSent ? 'Reset Password' : 'Generate OTP'"></span>
</button>
</form>
<div class="mt-6 text-center">
<a href="{{ \Filament\Facades\Filament::getPanel('admin')->getLoginUrl() }}"
class="text-amber-500 hover:underline font-medium text-sm">
Back to login
</a>
</div>
</div>
</body>
</html> --}}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Forgot Password</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js" defer></script>
</head>
<body class="bg-gray-50 flex items-center justify-center h-screen">
<div class="w-full max-w-md bg-white p-8 rounded-2xl shadow-lg"
x-data="{
email: '',
showFields: false,
error: '',
success: ''
}">
<!-- Logo & Heading -->
<div class="text-center mb-6">
<img src="{{ asset('/assets/crilogo1.png') }}" alt="Logo" class="mx-auto w-24 h-24">
{{-- <img src="{{ Storage::url('app/private/uploads/CRI/crilogo1.png') }}" alt="Logo" class="mx-auto w-24 h-24"> --}}
<h1 class="mt-4 text-2xl font-bold text-gray-700">Forgot Password</h1>
<p class="text-gray-500 mt-1 text-sm">Enter your email to reset your password</p>
</div>
<!-- Frontend error / success -->
<div class="mb-4 text-red-600 text-sm text-center" x-show="error" x-text="error"></div>
<div class="mb-4 text-green-600 text-sm text-center" x-show="success" x-text="success"></div>
<!-- Email & Password Form -->
<form x-data="{
email: '',
old_password: '',
password: '',
password_confirmation: '',
showFields: false,
emailError: '',
passwordError: '',
oldPasswordError: '',
newPasswordError: '',
error: '',
success: ''
}"
x-on:submit.prevent="
oldPasswordError=''; newPasswordError=''; error=''; passwordError=''; success='';
fetch('{{ route('filament.admin.forgot-password.otp') }}', {
method:'POST',
headers:{
'Content-Type':'application/json',
'X-CSRF-TOKEN':'{{ csrf_token() }}'
},
body: JSON.stringify({
email: email,
old_password: old_password,
password: password,
password_confirmation: password_confirmation
})
})
.then(res=>res.json())
.then(data => {
if(data.success){
// Success
success = data.success;
old_password = '';
password = '';
password_confirmation = '';
} else {
oldPasswordError = data.oldPasswordError || '';
newPasswordError = data.newPasswordError || '';
passwordError = data.passwordError || '';
}
})
.catch(() => {
passwordError = 'Something Went Wrong.';
});
"
>
@csrf
<!-- Email -->
<div class="mb-4">
<label class="block text-gray-700 text-sm font-medium mb-2">Email</label>
<input type="email" x-model="email" :readonly="showFields"
@keydown.enter.prevent="$refs.continueBtn.click()"
class="w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-amber-500 focus:border-amber-500">
<div class="text-red-600 text-sm mt-1" x-text="emailError" x-show="emailError"></div>
</div>
<!-- Continue button (Check email) -->
<button type="button" x-show="!showFields" x-ref="continueBtn"
class="w-full py-2 px-4 bg-amber-500 text-white font-semibold rounded-lg shadow-md hover:bg-amber-600 transition duration-200"
x-on:click="
emailError=''; success='';
if(!email){ emailError='Please enter your email'; return; }
// Simple email regex check
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if(!emailPattern.test(email)){
emailError='Please enter a valid email';
return;
}
fetch('{{ route('admin.check-email') }}',{
method:'POST',
headers:{
'Content-Type':'application/json',
'X-CSRF-TOKEN':'{{ csrf_token() }}'
},
body:JSON.stringify({email: email})
})
.then(res=>res.json())
.then(data=>{
if(data.exists){
showFields=true;
} else {
emailError='No user found with this email.';
}
})
.catch(()=> emailError='Something went wrong.');
"
>Continue</button>
<!-- Old / New / Confirm Password -->
<div x-show="showFields" x-transition class="mt-4 space-y-4">
{{-- <div>
<label class="block text-gray-700 text-sm font-medium mb-2">Old Password</label>
<input type="password" x-model="old_password" required
class="w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-amber-500 focus:border-amber-500">
<div class="text-red-600 text-sm mt-1" x-text="oldPasswordError" x-show="oldPasswordError"></div>
</div> --}}
<!-- Old Password -->
<div x-data="{ show: false }" class="relative">
<label class="block text-gray-700 text-sm font-medium mb-2">Old Password</label>
<input :type="show ? 'text' : 'password'" x-model="old_password"
class="w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-amber-500 focus:border-amber-500">
<button type="button" @click="show = !show" class="absolute right-3 top-8 flex items-center text-gray-500">
<svg x-show="!show" xmlns="http://www.w3.org/2000/svg" class="h-7 w-7" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.477 0 8.268 2.943 9.542 7-1.274 4.057-5.065 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
</svg>
<svg x-show="show" xmlns="http://www.w3.org/2000/svg" class="h-7 w-7" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.269-2.944-9.543-7a10.05 10.05 0 012.175-3.293M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3l18 18" />
</svg>
</button>
<div class="text-red-600 text-sm mt-1" x-text="oldPasswordError" x-show="oldPasswordError"></div>
</div>
{{-- <div>
<label class="block text-gray-700 text-sm font-medium mb-2">New Password</label>
<input type="password" x-model="password" required
class="w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-amber-500 focus:border-amber-500">
</div> --}}
<div x-data="{ show: false }" class="relative">
<label class="block text-gray-700 text-sm font-medium mb-2">New Password</label>
<input :type="show ? 'text' : 'password'" x-model="password" required
class="w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-amber-500 focus:border-amber-500">
<button type="button" @click="show = !show"
class="absolute right-3 top-8 flex items-center text-gray-500">
<!-- Eye open -->
<svg x-show="!show" xmlns="http://www.w3.org/2000/svg" class="h-7 w-7" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M2.458 12C3.732 7.943 7.523 5 12 5c4.477 0 8.268 2.943 9.542 7-1.274 4.057-5.065 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
</svg>
<!-- Eye closed -->
<svg x-show="show" xmlns="http://www.w3.org/2000/svg" class="h-7 w-7" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.269-2.944-9.543-7a10.05 10.05 0 012.175-3.293M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3l18 18" />
</svg>
</button>
</div>
{{-- <div>
<label class="block text-gray-700 text-sm font-medium mb-2">Confirm Password</label>
<input type="password" x-model="password_confirmation" required
class="w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-amber-500 focus:border-amber-500">
<div class="text-red-600 text-sm mt-1" x-text="newPasswordError" x-show="newPasswordError"></div>
</div> --}}
<div x-data="{ show: false }" class="relative">
<label class="block text-gray-700 text-sm font-medium mb-2">Confirm Password</label>
<input :type="show ? 'text' : 'password'" x-model="password_confirmation" required
class="w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-amber-500 focus:border-amber-500">
<button type="button" @click="show = !show"
class="absolute right-3 top-8 flex items-center text-gray-500">
<!-- Eye open -->
<svg x-show="!show" xmlns="http://www.w3.org/2000/svg" class="h-7 w-7" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M2.458 12C3.732 7.943 7.523 5 12 5c4.477 0 8.268 2.943 9.542 7-1.274 4.057-5.065 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
</svg>
<!-- Eye closed -->
<svg x-show="show" xmlns="http://www.w3.org/2000/svg" class="h-7 w-7" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.269-2.944-9.543-7a10.05 10.05 0 012.175-3.293M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3l18 18" />
</svg>
</button>
<div class="text-red-600 text-sm mt-1" x-text="newPasswordError" x-show="newPasswordError"></div>
</div>
<button type="submit"
class="w-full py-2 px-4 bg-green-500 text-white font-semibold rounded-lg hover:bg-green-600 transition duration-200">
Update Password
</button>
<div class="text-green-600 text-sm mt-2" x-text="success" x-show="success"></div>
<!-- Old Password Error -->
</div>
</form>
<!-- Flash messages -->
@if(session('status'))
<div class="mt-2 text-green-600 text-sm text-center">
{{ session('status') }}
</div>
@endif
@if($errors->any())
<div class="mt-2 text-red-600 text-sm text-center">
{{ $errors->first() }}
</div>
@endif
<div class="mt-6 text-center">
<a href="{{ \Filament\Facades\Filament::getPanel('admin')->getLoginUrl() }}"
class="text-amber-500 hover:underline font-medium text-sm">
Back to login
</a>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,28 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Reset Password</title>
</head>
<body>
<h1>Reset Password</h1>
<form method="POST" action="{{ route('password.update') }}">
@csrf
<input type="hidden" name="token" value="{{ $token }}">
<label>Email:</label>
<input type="email" name="email" value="{{ old('email') }}" required>
<label>New Password:</label>
<input type="password" name="password" required>
<label>Confirm Password:</label>
<input type="password" name="password_confirmation" required>
<button type="submit">Reset Password</button>
</form>
<a href="{{ route('login') }}">Back to login</a>
</body>
</html>

View File

@@ -0,0 +1,48 @@
<div class="sticky top-0 z-20 bg-white dark:bg-gray-900 px-2 py-2 shadow-sm" x-data="sidebarSearch()">
<x-filament::input.wrapper class="relative">
<x-filament::input
type="text"
placeholder="Search…"
x-ref="search"
x-on:input.debounce.300ms="filterItems($event.target.value)"
x-on:keydown.escape="clearSearch"
class="w-full rounded border px-2 py-1 text-sm"
/>
</x-filament::input.wrapper>
<script>
document.addEventListener('alpine:init', () => {
Alpine.data('sidebarSearch', () => ({
init() {
this.$refs.search.value = ''
},
filterItems(searchTerm) {
const groups = document.querySelectorAll('.fi-sidebar-nav-groups .fi-sidebar-group')
searchTerm = searchTerm.toLowerCase()
groups.forEach(group => {
const groupButton = group.querySelector('.fi-sidebar-group-button')
const groupText = (groupButton?.textContent || '').toLowerCase()
const items = group.querySelectorAll('.fi-sidebar-item')
let hasVisibleItems = false
const groupMatches = groupText.includes(searchTerm)
items.forEach(item => {
const itemText = item.textContent.toLowerCase()
const isVisible = itemText.includes(searchTerm) || groupMatches
item.style.display = isVisible ? '' : 'none'
if (isVisible) hasVisibleItems = true
})
group.style.display = (hasVisibleItems || groupMatches) ? '' : 'none'
})
},
clearSearch() {
this.$refs.search.value = ''
this.filterItems('')
},
}))
})
</script>
</div>

View File

@@ -0,0 +1,170 @@
{{-- <input
type="file"
id="folderPicker"
webkitdirectory
directory
multiple
style="display:none"
/>
<script>
function openFolderPicker() {
const input = document.getElementById('folderPicker');
input.value = ''; // reset
input.click(); // open system folder dialog
input.onchange = function(event) {
const files = event.target.files;
console.log("Files selected:", files);
alert(`${files.length} files selected`);
};
}
</script> --}}
<!-- Hidden input for folder picker -->
{{-- <input
type="file"
id="folderPicker"
webkitdirectory
directory
multiple
accept=".txt"
style="display:none"
/>
<script>
function openFolderPicker() {
const input = document.getElementById('folderPicker');
input.value = '';
input.click();
input.onchange = function(event) {
const files = Array.from(event.target.files);
const txtFiles = files.filter(f => f.name.endsWith('.txt'));
if (txtFiles.length === 0) {
alert("No .txt files found in selected folder!");
return;
}
txtFiles.forEach(file => {
const reader = new FileReader();
reader.onload = function(e) {
const content = e.target.result;
console.log("File:", file.name, "Content:", content);
alert(`File: ${file.name}\nContent:\n${content}`);
};
reader.readAsText(file);
});
};
}
</script> --}}
<!-- Hidden file input -->
{{-- <input type="file" id="fileInput" style="display:none" />
<script>
function openFileDialog() {
const input = document.getElementById('fileInput');
input.value = ''; // reset
input.click(); // open system file dialog
input.onchange = function(event) {
const file = event.target.files[0]; // get selected file
if (!file) {
alert("No file selected!");
return;
}
const reader = new FileReader();
reader.onload = function(e) {
const content = e.target.result;
console.log("File content:", content);
// Show content in page (or Filament modal)
alert(`File: ${file.name}\n\nContent:\n${content}`);
};
reader.onerror = function() {
alert("Failed to read file!");
};
reader.readAsText(file); // read as text
};
}
</script> --}}
<!-- Hidden file input -->
<input type="file" id="fileInput" style="display:none" />
<script>
function openFileDialog() {
const input = document.getElementById('fileInput');
input.value = '';
input.click();
input.onchange = function(event) {
const file = event.target.files[0];
if (!file) {
alert("No file selected!");
return;
}
const reader = new FileReader();
reader.onload = function(e) {
const content = e.target.result;
// Split file lines into array
const lines = content.split(/\r?\n/).map(l => l.trim());
const payload = {
name: lines[0] ?? '',
identification1: lines[1] ?? '',
identification2: lines[2] ?? '',
identification3: lines[3] ?? '',
contact_number: lines[4] ?? '',
alternate_number: lines[5] ?? '',
};
console.log("Payload to send:", payload);
// Send to backend
fetch("{{ route('file.store') }}", {
method: "POST",
headers: {
"X-CSRF-TOKEN": "{{ csrf_token() }}",
"Content-Type": "application/json"
},
body: JSON.stringify(payload)
})
.then(res => res.json())
.then(data => {
if (data.success) {
alert("DriverMaster record saved successfully!");
} else {
alert("Failed to save record.");
}
})
.catch(err => {
console.error(err);
alert("Error saving record.");
});
};
reader.onerror = function() {
alert("Failed to read file!");
};
reader.readAsText(file);
};
}
</script>

View File

@@ -0,0 +1,31 @@
{{-- @if ($getState())
<div class="p-4 text-center">
<img src="{{ $getState() }}"
onerror="this.onerror=null; this.src='{{ asset('images/not-found.png') }}'"
class="rounded shadow mx-auto"
style="width: 400px; height: 400px; object-fit: contain;" />
</div>
@else
<div class="p-4 text-center">
<img src="{{ asset('images/not-found.png') }}"
class="rounded shadow mx-auto"
style="width: 400px; height: 400px; object-fit: contain;" />
</div>
@endif --}}
@if ($getState())
<div class="p-4 text-center">
<img src="{{ $getState() }}?v={{ time() }}"
onerror="this.onerror=null; this.src='{{ asset('images/not-found.png') }}'"
class="rounded shadow mx-auto"
style="width: 200px; height: 200px; object-fit: contain;" />
</div>
@else
<div class="p-4 text-center">
<img src="{{ asset('images/not-found.png') }}"
class="rounded shadow mx-auto"
style="width: 200px; height: 200px; object-fit: contain;" />
</div>
@endif

View File

@@ -0,0 +1,42 @@
{{-- @if ($getState())
<div class="p-4 text-center">
<img src="{{ $getState() }}"
class="rounded shadow mx-auto"
style="width: 400px; height: 400px; object-fit: contain;" />
</div>
@endif --}}
{{-- @if (!empty($image))
<div class="mt-2">
<img src="{{ $image }}" alt="Part Validation Error Image" style="width: 200px; height: auto;">
</div>
@endif --}}
{{-- @if (!empty($part_validation1_error_image))
<div class="mt-2">
<img src="{{ $image }}" alt="Part Validation Error Image" style="width: 200px; height: auto;">
</div>
@else
<div class="mt-2">
<img src="{{ asset('images/not-found.png') }}" alt="Image Not Found" style="width: 200px; height: auto;">
</div>
@endif --}}
@if ($getState())
<div class="p-4 text-center">
<img src="{{ $getState() }}?v={{ time() }}"
onerror="this.onerror=null; this.src='{{ asset('images/not-found.png') }}'"
class="rounded shadow mx-auto"
style="width: 200px; height: 200px; object-fit: contain;" />
</div>
@endif
{{-- @else
<img src="{{ asset('images/not-found.png') }}" style="width: 200px; height: auto;">
@endif --}}

View File

@@ -0,0 +1,177 @@
<!DOCTYPE html>
<html>
<head>
<style>
@page { size: A3 landscape; margin: 30px 8px 8px 8px; }
body {
font-family: Arial, sans-serif;
font-size: 8px;
counter-reset: page;
}
.page-number {
position: fixed;
top: -15px;
right: 0;
font-size: 10px;
}
.page-number:after {
content: "Page " counter(page);
}
.data-table {
width: 100%;
border-collapse: collapse;
margin-top: 6px;
}
.data-table th, .data-table td {
border: 1px solid #222;
padding: 2px;
text-align: center;
font-size: 7px;
}
thead { display: table-header-group; }
.company-title { font-size: 16px; font-weight: bold; text-align: center; }
.company-subtitle { font-size: 12px; text-align: center; }
.company-address { font-size: 10px; text-align: center; }
.register-title { font-size: 12px; font-weight: bold; text-align: center; }
</style>
</head>
<body>
<div class="page-number"></div>
<table class="data-table">
<tr>
<td colspan="20" style="padding:0;">
<table width="100%" style="border-collapse:collapse;">
<tr>
<td style="width:13%; text-align:left; border:none; border-bottom:1px solid #222;">
<img src="{{ public_path('images/cripumps.logo.png') }}" alt="Logo" height="45" style="position: relative; left: 5mm;">
</td>
<td style="width:74%; border:none; border-bottom:1px solid #222;">
<div class="company-title">C.R.I. Pumps Private Limited</div>
<div class="company-subtitle">Unit : {{ $plant?->name ?? '' }}</div>
<div class="company-address">{{ $plant?->address ?? '' }}</div>
<div class="register-title">MOTOR FREE RUN TEST REGISTER</div>
</td>
{{-- @php
$showIsiLogo = $records->every(fn ($record) => $record['isi_model']);
@endphp
<td style="width:13%; text-align:right; border:none; border-bottom:1px solid #222;">
@if ($showIsiLogo)
<img src="{{ public_path('images/isi_logo1.PNG') }}" alt="ISI Logo" height="35" style="position: relative; left: -20mm;">
@endif
</td> --}}
<td style="width:13%; text-align:right; border:none; border-bottom:1px solid #222;"> </td>
</tr>
<tr>
@php
$uniqueItemCodes = $records->pluck('Item Code')->unique();
$kw = $hp = $phase = '-';
if ($uniqueItemCodes->count() === 1) {
$firstRecord = $records->first();
$kw = $firstRecord['kw'] ?? '-';
$hp = $firstRecord['hp'] ?? '-';
$phase = $firstRecord['phase'] ?? '-';
}
@endphp
<td colspan="3" style="text-align:left; font-size:10px; font-weight:bold; border:none; padding-left:300mm;">
MOTOR KW / HP : {{ $kw }} / {{ $hp }} and PHASE : {{ $phase }}
</td>
</tr>
</table>
</td>
</tr>
<thead>
<!-- Row 1: Main Group Headings -->
<tr>
{{-- <th rowspan="3">Date</th> --}}
<th rowspan="3" style="width: 40px; white-space: nowrap;">Date</th>
<th rowspan="3">Motor SNo</th>
<th rowspan="3">Item Code</th>
<th rowspan="3">Motor Type</th>
<th colspan="8">AFTER FREE RUN</th>
<th colspan="3">LOCKED ROTOR TEST</th>
<th rowspan="3">No Load Pickup Voltage</th>
<th rowspan="3">Room Temp.</th>
<th rowspan="3">High Voltage Test</th>
<th rowspan="3">Result</th>
<th rowspan="3">Remark</th>
</tr>
<!-- Row 2: Sub-Headers (Column Names) -->
<tr>
<!-- AFTER FREE RUN -->
<th>Voltage</th>
<th>Current</th>
<th>Power</th>
<th>IR.Hot</th>
<th>IR.Cool</th>
<th>Frequency</th>
<th>Speed</th>
<th>Leakage Current</th>
<!-- LOCKED ROTOR TEST -->
<th>Voltage</th>
<th>Current</th>
<th>Power</th>
</tr>
<tr>
<!-- AFTER FREE RUN -->
<td>(V)</td>
<td>(A)</td>
<td>(W)</td>
<td>(Ohm)</td>
<td>(Ohm)</td>
<td>(Hz)</td>
<td>(Rpm)</td>
<td>(mA)</td>
<!-- LOCKED ROTOR TEST -->
<td>(V)</td>
<td>(A)</td>
<td>(W)</td>
</tr>
</thead>
@foreach($records as $record)
<tr>
<td>{{ $record['Date'] ?? '' }}</td>
<td>{{ $record['Motor SNo'] ?? '' }}</td>
<td>{{ $record['Item Code'] ?? '' }}</td>
<td style="white-space: nowrap;">{{ $record['Motor Type'] ?? '' }}</td>
{{-- AFTER FREE RUN --}}
<td>{{ $record['Voltage_After'] ?? '' }}</td>
<td>{{ $record['Current_After'] ?? '' }}</td>
<td>{{ $record['Power_After'] ?? '' }}</td>
<td>{{ $record['IR_Hot'] ?? '' }}</td>
<td>{{ $record['IR_Cool'] ?? '' }}</td>
<td>{{ $record['Frequency_After'] ?? '' }}</td>
<td>{{ $record['Speed_After'] ?? '' }}</td>
<td>{{ $record['Leakage_Current'] ?? '' }}</td>
{{-- LOCKED ROTOR TEST --}}
<td>{{ $record['Voltage_Locked'] ?? '' }}</td>
<td>{{ $record['Current_Locked'] ?? '' }}</td>
<td>{{ $record['Power_Locked'] ?? '' }}</td>
{{-- OTHERS --}}
<td>{{ $record['No_Load_Pickup_Voltage'] ?? '' }}</td>
<td>{{ $record['Room_Temp'] ?? '' }}</td>
<td style="white-space: nowrap;">{{ $record['High_Voltage_Test'] ?? '' }}</td>
<td>{{ $record['Result'] ?? '' }}</td>
<td>{{ $record['Remark'] ?? '' }}</td>
</tr>
@endforeach
</table>
</body>
</html>

View File

@@ -0,0 +1,202 @@
<!DOCTYPE html>
<html>
<head>
<style>
@page { size: A3 landscape; margin: 30px 8px 8px 8px; }
body {
font-family: Arial, sans-serif;
font-size: 8px;
counter-reset: page;
}
.page-number {
position: fixed;
top: -15px;
right: 0;
font-size: 10px;
}
.page-number:after {
content: "Page " counter(page);
}
.data-table {
width: 100%;
border-collapse: collapse;
margin-top: 6px;
}
.data-table th, .data-table td {
border: 1px solid #222;
padding: 2px;
text-align: center;
font-size: 7px;
}
thead { display: table-header-group; }
.company-title { font-size: 16px; font-weight: bold; text-align: center; }
.company-subtitle { font-size: 12px; text-align: center; }
.company-address { font-size: 10px; text-align: center; }
.register-title { font-size: 12px; font-weight: bold; text-align: center; }
</style>
</head>
<body>
<div class="page-number"></div>
<table class="data-table">
<tr>
<td colspan="24" style="padding:0;">
<table width="100%" style="border-collapse:collapse;">
<tr>
<td style="width:13%; text-align:left; border:none; border-bottom:1px solid #222;">
<img src="{{ public_path('images/cripumps.logo.png') }}" alt="Logo" height="45" style="position: relative; left: 5mm;">
</td>
<td style="width:74%; border:none; border-bottom:1px solid #222;">
<div class="company-title">C.R.I. Pumps Private Limited</div>
<div class="company-subtitle">Unit : {{ $plant?->name ?? '' }}</div>
<div class="company-address">{{ $plant?->address ?? '' }}</div>
<div class="register-title">MOTOR FREE RUN TEST REGISTER</div>
</td>
{{-- @php
$showIsiLogo = $records->every(fn ($record) => $record['isi_model']);
@endphp --}}
{{-- <td style="width:13%; text-align:right; border:none; border-bottom:1px solid #222;">
@if ($showIsiLogo)
<img src="{{ public_path('images/isi_logo1.PNG') }}" alt="ISI Logo" height="35" style="position: relative; left: -20mm;">
@endif
</td> --}}
<td style="width:13%; text-align:right; border:none; border-bottom:1px solid #222;"> </td>
</tr>
<tr>
@php
$uniqueItemCodes = $records->pluck('Item Code')->unique();
$kw = $hp = $phase = '-';
if ($uniqueItemCodes->count() === 1) {
$firstRecord = $records->first();
$kw = $firstRecord['kw'] ?? '-';
$hp = $firstRecord['hp'] ?? '-';
$phase = $firstRecord['phase'] ?? '-';
}
@endphp
<td colspan="3" style="text-align:left; font-size:10px; font-weight:bold; border:none; padding-left:300mm;">
MOTOR KW / HP : {{ $kw }} / {{ $hp }} and PHASE : {{ $phase }}
</td>
</tr>
</table>
</td>
</tr>
<thead>
<!-- Row 1: Main Group Headings -->
<tr>
{{-- <th rowspan="4">Date</th> --}}
<th rowspan="4" style="width: 40px; white-space: nowrap;">Date</th>
<th rowspan="4">Motor SNo</th>
<th rowspan="4">Item Code</th>
<th rowspan="4">Motor Type</th>
<th colspan="12">AFTER FREE RUN</th>
<th colspan="3">LOCKED ROTOR TEST</th>
<th rowspan="4">No Load Pickup Voltage</th>
<th rowspan="4">Room Temp.</th>
<th rowspan="4">High Voltage Test</th>
<th rowspan="4">Result</th>
<th rowspan="4">Remark</th>
</tr>
<!-- Row 2: AFTER FREE RUN main columns -->
<tr>
<th rowspan="2">Voltage</th>
<th rowspan="2">Current</th>
<th rowspan="2">Power</th>
<th colspan="3">IR.Hot</th>
<th colspan="3">IR.Cool</th>
<th rowspan="2">Frequency</th>
<th rowspan="2">Speed</th>
<th rowspan="2">Leakage Current</th>
<!-- LOCKED ROTOR TEST -->
<th rowspan="2">Voltage</th>
<th rowspan="2">Current</th>
<th rowspan="2">Power</th>
</tr>
<!-- Row 3: Sub-columns for IR.Hot and IR.Cool -->
<tr>
<th>R</th>
<th>Y</th>
<th>B</th>
<th>R</th>
<th>Y</th>
<th>B</th>
</tr>
<!-- Row 4: Units -->
<tr>
<td>(V)</td> <!-- Voltage -->
<td>(A)</td> <!-- Current -->
<td>(W)</td> <!-- Power -->
<td>(Ohm)</td> <!-- IR.Hot R -->
<td>(Ohm)</td> <!-- IR.Hot Y -->
<td>(Ohm)</td> <!-- IR.Hot B -->
<td>(Ohm)</td> <!-- IR.Cool R -->
<td>(Ohm)</td> <!-- IR.Cool Y -->
<td>(Ohm)</td> <!-- IR.Cool B -->
<td>(Hz)</td> <!-- Frequency -->
<td>(Rpm)</td> <!-- Speed -->
<td>(mA)</td> <!-- Leakage Current -->
<td>(V)</td> <!-- Locked Voltage -->
<td>(A)</td> <!-- Locked Current -->
<td>(W)</td> <!-- Locked Power -->
</tr>
</thead>
@foreach($records as $record)
<tr>
{{-- COMMON FIELDS --}}
<td>{{ $record['Date'] ?? '' }}</td>
<td>{{ $record['Motor SNo'] ?? '' }}</td>
<td>{{ $record['Item Code'] ?? '' }}</td>
<td style="white-space: nowrap;">{{ $record['Motor Type'] ?? '' }}</td>
{{-- AFTER FREE RUN --}}
<td>{{ $record['Voltage_After'] ?? '' }}</td>
<td>{{ $record['Current_After'] ?? '' }}</td>
<td>{{ $record['Power_After'] ?? '' }}</td>
{{-- IR Hot: R, Y, B --}}
<td>{{ $record['IR_Hot_R'] ?? '' }}</td>
<td>{{ $record['IR_Hot_Y'] ?? '' }}</td>
<td>{{ $record['IR_Hot_B'] ?? '' }}</td>
{{-- IR Cool: R, Y, B --}}
<td>{{ $record['IR_Cool_R'] ?? '' }}</td>
<td>{{ $record['IR_Cool_Y'] ?? '' }}</td>
<td>{{ $record['IR_Cool_B'] ?? '' }}</td>
<td>{{ $record['Frequency_After'] ?? '' }}</td>
<td>{{ $record['Speed_After'] ?? '' }}</td>
<td>{{ $record['Leakage_Current'] ?? '' }}</td>
{{-- LOCKED ROTOR TEST --}}
<td>{{ $record['Voltage_Locked'] ?? '' }}</td>
<td>{{ $record['Current_Locked'] ?? '' }}</td>
<td>{{ $record['Power_Locked'] ?? '' }}</td>
{{-- OTHER FIELDS --}}
<td>{{ $record['No_Load_Pickup_Voltage'] ?? '' }}</td>
<td>{{ $record['Room_Temp'] ?? '' }}</td>
<td style="white-space: nowrap;">{{ $record['High_Voltage_Test'] ?? '' }}</td>
<td>{{ $record['Result'] ?? '' }}</td>
<td>{{ $record['Remark'] ?? '' }}</td>
</tr>
@endforeach
</table>
</body>
</html>

View File

@@ -0,0 +1,219 @@
<!DOCTYPE html>
<html>
<head>
<style>
@page { size: A3 landscape; margin: 30px 8px 8px 8px; }
body { font-family: Arial, sans-serif; font-size: 8px; }
body {
font-family: Arial, sans-serif;
font-size: 8px;
counter-reset: page;
}
/* Page number fixed at top-right, slightly above the content */
.page-number {
position: fixed;
top: -15px; /* Adjust as needed to appear above the border */
right: 0;
font-size: 10px;
}
.page-number:after {
content: "Page " counter(page);
}
.header-table { width: 100%; border-collapse: collapse;}
.header-table td { vertical-align: middle; border-collapse: collapse; border: 1px solid #222; }
.company-title { font-size: 16px; font-weight: bold; text-align: center; }
.company-subtitle { font-size: 12px; text-align: center; }
.company-address { font-size: 10px; text-align: center; }
.register-title { font-size: 12px; font-weight: bold; text-align: center; }
.data-table { width: 100%; border-collapse: collapse; margin-top: 6px; }
.data-table th, .data-table td { border: 1px solid #222; padding: 2px 2px; text-align: center; font-size: 7px; }
.data-table td:last-child {border-right: 1px solid #222 !important;}
/* .highlight { color: #0072c6; font-weight: bold; } */
thead { display: table-header-group; }
</style>
</head>
<body>
<div class="page-number"></div>
<table class="data-table">
<tr>
<td colspan="39" style="padding:0;">
<table width="100%" style="border-collapse:collapse;">
<tr>
<td style="width:13%; text-align:left; border:none; border-bottom:1px solid #222;">
<img src="{{ public_path('images/cripumps.logo.png') }}" alt="Left Logo" height="45" style="position: relative; left: 5mm;">
</td>
<td style="width:74%; border:none; border-bottom:1px solid #222;">
<div class="company-title">C.R.I. Pumps Private Limited</div>
<div class="company-subtitle">Unit : {{ $plant?->name ?? '' }}</div>
<div class="company-address">{{ $plant?->address ?? '' }}</div>
<div class="register-title">MOTOR FREE RUN TEST REGISTER</div>
</td>
{{-- <td style="width:13%; text-align:right; border:none; border-bottom:1px solid #222;">
<img src="{{ public_path('images/isi_logo1.PNG') }}" alt="ISI Logo" height="35" style="position: relative; left: -20mm;">
</td> --}}
{{-- @php
$showIsiLogo = $records->every(fn ($record) => $record['isi_model']);
@endphp
@if ($showIsiLogo)
<td style="width:13%; text-align:right; border:none; border-bottom:1px solid #222;">
<img src="{{ public_path('images/isi_logo1.PNG') }}" alt="ISI Logo" height="35" style="position: relative; left: -20mm;">
</td>
@else
<td style="width:13%; border:none; border-bottom:1px solid #222;"></td>
@endif --}}
<td style="width:13%; border:none; border-bottom:1px solid #222;"></td>
</tr>
<tr>
{{-- @php
$firstRecord = $records->first();
$kw = $firstRecord['kw'] ?? '-';
$hp = $firstRecord['hp'] ?? '-';
$phase = $firstRecord['phase'] ?? '-';
@endphp --}}
@php
$uniqueItemCodes = $records->pluck('Item Code')->unique();
//Default values
$kw = '-';
$hp = '-';
$phase = '-';
// If there's exactly one unique item code, extract values from the first record
if ($uniqueItemCodes->count() === 1) {
$firstRecord = $records->first();
$kw = $firstRecord['kw'] ?? '-';
$hp = $firstRecord['hp'] ?? '-';
$phase = $firstRecord['phase'] ?? '-';
}
@endphp
<td colspan="3" style="text-align:left; font-size:10px; font-weight:bold; border:none; padding-left:300mm;">
MOTOR KW / HP : {{ $kw }} / {{ $hp }} and PHASE : {{ $phase }}
</td>
</tr>
</table>
</td>
</tr>
<thead>
<!-- Table Column Headers -->
<tr>
{{-- <th rowspan="3">Date</th> --}}
<th rowspan="3" style="width: 40px; white-space: nowrap;">Date</th>
<th rowspan="3">Output</th>
<th rowspan="3">Motor SNo</th>
<th rowspan="3">Item Code</th>
{{-- <th rowspan="3">Motor Type</th> --}}
<th rowspan="3" style="white-space: nowrap;">Motor Type</th>
<th colspan="11">BEFORE FREE RUN</th>
<th colspan="12">AFTER FREE RUN</th>
<th colspan="3">LOCKED ROTOR TEST</th>
<th rowspan="3">No Load Pickup Voltage (V)</th>
<th rowspan="3">Room Temp. (°C)</th>
<th rowspan="3">High Voltage Test (V)</th>
<th rowspan="3">Batch Number</th>
<th rowspan="3">Batch Count</th>
<th rowspan="3">Result</th>
<th rowspan="3">Remark</th>
<th rowspan="3">Tested By</th>
</tr>
<tr>
<th rowspan="2">Voltage (V)</th>
<th rowspan="2">Current (A)</th>
<th rowspan="2">Power (W)</th>
<th colspan="3">Resistance (Ohm)</th>
<th colspan="3">Insulation Resistance (Ohm)</th>
<th rowspan="2">Frequency (Hz)</th>
<th rowspan="2">Speed (Rpm)</th>
<th rowspan="2">Voltage (V)</th>
<th rowspan="2">Current (A)</th>
<th rowspan="2">Power (W)</th>
<th colspan="3">IR.Hot (Ohm)</th>
<th colspan="3">IR.Cool (Ohm)</th>
<th rowspan="2">Frequency (Hz)</th>
<th rowspan="2">Speed (Rpm)</th>
<th rowspan="2">Leakage Current (mA)</th>
<th rowspan="2">Voltage (V)</th>
<th rowspan="2">Current (A)</th>
<th rowspan="2">Power (W)</th>
</tr>
<tr>
<th>RY</th>
<th>YB</th>
<th>BR</th>
<th>R</th>
<th>Y</th>
<th>B</th>
<th>R</th>
<th>Y</th>
<th>B</th>
<th>R</th>
<th>Y</th>
<th>B</th>
</tr>
</thead>
<tbody>
@foreach ($records as $record)
<tr>
<td>{{ $record['Date'] ?? '' }}</td>
<td>{{ $record['Output'] ?? '' }}</td>
<td>{{ $record['Motor SNo'] ?? '' }}</td>
<td>{{ $record['Item Code'] ?? '' }}</td>
{{-- <td>{{ $record['Motor Type'] ?? '' }}</td> --}}
<td style="white-space: nowrap;">{{ $record['Motor Type'] ?? '' }}</td>
{{-- BEFORE FREE RUN --}}
<td>{{ $record['Voltage_Before'] ?? '' }}</td>
<td>{{ $record['Current_Before'] ?? '' }}</td>
<td>{{ $record['Power_Before'] ?? '' }}</td>
<td>{{ $record['Resistance_RY'] ?? '' }}</td>
<td>{{ $record['Resistance_YB'] ?? '' }}</td>
<td>{{ $record['Resistance_BR'] ?? '' }}</td>
<td>{{ $record['Insulation_Resistance_R'] ?? '' }}</td>
<td>{{ $record['Insulation_Resistance_Y'] ?? '' }}</td>
<td>{{ $record['Insulation_Resistance_B'] ?? '' }}</td>
<td>{{ $record['Frequency_Before'] ?? '' }}</td>
<td>{{ $record['Speed_Before'] ?? '' }}</td>
{{-- AFTER FREE RUN --}}
<td>{{ $record['Voltage_After'] ?? '' }}</td>
<td>{{ $record['Current_After'] ?? '' }}</td>
<td>{{ $record['Power_After'] ?? '' }}</td>
<td>{{ $record['IR_Hot_R'] ?? '' }}</td>
<td>{{ $record['IR_Hot_Y'] ?? '' }}</td>
<td>{{ $record['IR_Hot_B'] ?? '' }}</td>
<td>{{ $record['IR_Cool_R'] ?? '' }}</td>
<td>{{ $record['IR_Cool_Y'] ?? '' }}</td>
<td>{{ $record['IR_Cool_B'] ?? '' }}</td>
<td>{{ $record['Frequency_After'] ?? '' }}</td>
<td>{{ $record['Speed_After'] ?? '' }}</td>
<td>{{ $record['Leakage_Current'] ?? '' }}</td>
{{-- LOCKED ROTOR TEST --}}
<td>{{ $record['Voltage_Locked'] ?? '' }}</td>
<td>{{ $record['Current_Locked'] ?? '' }}</td>
<td>{{ $record['Power_Locked'] ?? '' }}</td>
{{-- Other Info --}}
<td>{{ $record['No_Load_Pickup_Voltage'] ?? '' }}</td>
<td>{{ $record['Room_Temp'] ?? '' }}</td>
<td style="white-space: nowrap;">{{ $record['High_Voltage_Test'] ?? '' }}</td>
<td>{{ $record['Batch_Number'] ?? '' }}</td>
<td>{{ $record['Batch_Count'] ?? '' }}</td>
<td>{{ $record['Result'] ?? '' }}</td>
<td>{{ $record['Remark'] ?? '' }}</td>
<td>{{ $record['Tested_By'] ?? '' }}</td>
</tr>
@endforeach
</tbody>
</table>
</body>
</html>

View File

@@ -0,0 +1,241 @@
<!DOCTYPE html>
<html>
<head>
<style>
@page { size: A3 landscape; margin: 30px 8px 8px 8px; }
body { font-family: Arial, sans-serif; font-size: 8px; }
body {
font-family: Arial, sans-serif;
font-size: 8px;
counter-reset: page;
}
/* Page number fixed at top-right, slightly above the content */
.page-number {
position: fixed;
top: -15px; /* Adjust as needed to appear above the border */
right: 0;
font-size: 10px;
}
.page-number:after {
content: "Page " counter(page);
}
/* .data-table {
width: auto;
table-layout: auto;
} */
.header-table { width: 100%; border-collapse: collapse;}
.header-table td { vertical-align: middle; border-collapse: collapse; border: 1px solid #222; }
.company-title { font-size: 16px; font-weight: bold; text-align: center; }
.company-subtitle { font-size: 12px; text-align: center; }
.company-address { font-size: 10px; text-align: center; }
.register-title { font-size: 12px; font-weight: bold; text-align: center; }
.data-table { width: 100%; border-collapse: collapse; margin-top: 6px; }
.data-table th, .data-table td { border: 1px solid #222; padding: 2px 2px; text-align: center; font-size: 7px; }
.data-table td:last-child {border-right: 1px solid #222 !important;}
/* .highlight { color: #0072c6; font-weight: bold; } */
thead { display: table-header-group; }
</style>
</head>
<body>
<div class="page-number"></div>
<!-- Header Row with Logos and Company Info -->
<table class="data-table">
<tr>
<td colspan="33" style="padding:0;">
<table width="100%" style="border-collapse:collapse;">
<tr>
<td style="width:13%; text-align:left; border:none; border-bottom:1px solid #222;">
<img src="{{ public_path('images/cripumps.logo.png') }}" alt="Left Logo" height="45" style="position: relative; left: 5mm;">
</td>
<td style="width:74%; border:none; border-bottom:1px solid #222;">
<div class="company-title">C.R.I. Pumps Private Limited</div>
<div class="company-subtitle">Unit : {{ $plant?->name ?? '' }}</div>
<div class="company-address">{{ $plant?->address ?? '' }}</div>
<div class="register-title">MOTOR FREE RUN TEST REGISTER</div>
</td>
{{-- <td style="width:13%; text-align:right; border:none; border-bottom:1px solid #222;">
<img src="{{ public_path('images/isi_logo1.PNG') }}" alt="ISI Logo" height="35" style="position: relative; left: -20mm;">
</td> --}}
{{-- @php
$showIsiLogo = $records->every(fn ($record) => $record['isi_model']);
@endphp
@if ($showIsiLogo)
<td style="width:13%; text-align:right; border:none; border-bottom:1px solid #222;">
<img src="{{ public_path('images/isi_logo1.PNG') }}" alt="ISI Logo" height="35" style="position: relative; left: -20mm;">
</td>
@else
<td style="width:13%; border:none; border-bottom:1px solid #222;"></td>
@endif --}}
<td style="width:13%; border:none; border-bottom:1px solid #222;"></td>
</tr>
<tr>
{{-- @php
$firstRecord = $records->first();
$kw = $firstRecord['kw'] ?? '-';
$hp = $firstRecord['hp'] ?? '-';
$phase = $firstRecord['phase'] ?? '-';
@endphp --}}
@php
$uniqueItemCodes = $records->pluck('Item Code')->unique();
//Default values
$kw = '-';
$hp = '-';
$phase = '-';
// If there's exactly one unique item code, extract values from the first record
if ($uniqueItemCodes->count() === 1) {
$firstRecord = $records->first();
$kw = $firstRecord['kw'] ?? '-';
$hp = $firstRecord['hp'] ?? '-';
$phase = $firstRecord['phase'] ?? '-';
}
@endphp
<td colspan="3" style="text-align:left; font-size:10px; font-weight:bold; border:none; padding-left:300mm;">
MOTOR KW / HP : {{ $kw }} / {{ $hp }} and PHASE : {{ $phase }}
</td>
</tr>
</table>
</td>
</tr>
<thead>
<tr>
<!-- Combine first 5 columns into one cell with rowspan 4 -->
{{-- <th rowspan="4">Date</th> --}}
<th rowspan="4" style="width: 40px; white-space: nowrap;">Date</th>
<th rowspan="4">Output</th>
<th rowspan="4">Motor SNo</th>
<th rowspan="4">Item Code</th>
{{-- <th rowspan="4">Motor Type</th> --}}
<th rowspan="4" style="white-space: nowrap;">Motor Type</th>
<!-- Grouped columns -->
<th colspan="9">BEFORE FREE RUN</th>
<th colspan="8">AFTER FREE RUN</th>
<th colspan="3">LOCKED ROTOR TEST</th>
<!-- Last 8 columns with rowspan 4 -->
<th rowspan="4">No Load Pickup Voltage</th>
<th rowspan="4">Room Temp.</th>
<th rowspan="4">High Voltage Test</th>
<th rowspan="4">Batch Number</th>
<th rowspan="4">Batch Count</th>
<th rowspan="4">Result</th>
<th rowspan="4">Remark</th>
<th rowspan="4">Tested By</th>
</tr>
<tr>
<!-- BEFORE FREE RUN -->
<th rowspan="2">Voltage</th>
<th rowspan="2">Current</th>
<th rowspan="2">Power</th>
<th colspan="3">Resistance</th>
<th rowspan="2">Insulation Resistance</th>
<th rowspan="2">Frequency</th>
<th rowspan="2">Speed</th>
<!-- AFTER FREE RUN -->
<th rowspan="2">Voltage</th>
<th rowspan="2">Current</th>
<th rowspan="2">Power</th>
<th rowspan="2">IR.Hot</th>
<th rowspan="2">IR.Cool</th>
<th rowspan="2">Frequency</th>
<th rowspan="2">Speed</th>
<th rowspan="2">Leakage Current</th>
<!-- LOCKED ROTOR TEST -->
<th rowspan="2">Voltage</th>
<th rowspan="2">Current</th>
<th rowspan="2">Power</th>
</tr>
<tr>
<th>RY</th>
<th>YB</th>
<th>BR</th>
</tr>
<tr>
<th>(V)</th>
<th>(A)</th>
<th>(W)</th>
<th>(Ohm)</th>
<th>(Ohm)</th>
<th>(Ohm)</th>
<th>(Ohm)</th>
<th>(Hz)</th>
<th>(Rpm)</th>
<th>(V)</th>
<th>(A)</th>
<th>(W)</th>
<th>(Ohm)</th>
<th>(Ohm)</th>
<th>(Hz)</th>
<th>(Rpm)</th>
<th>(mA)</th>
<th>(V)</th>
<th>(A)</th>
<th>(W)</th>
</tr>
</thead>
@foreach($records as $record)
<tr>
<td>{{ $record['Date'] ?? '' }}</td>
<td>{{ $record['Output'] ?? '' }}</td>
<td>{{ $record['Motor SNo'] ?? '' }}</td>
<td>{{ $record['Item Code'] ?? '' }}</td>
{{-- <td>{{ $record['Motor Type'] ?? '' }}</td> --}}
<td style="white-space: nowrap;">{{ $record['Motor Type'] ?? '' }}</td>
{{-- BEFORE FREE RUN --}}
<td>{{ $record['Voltage_Before'] ?? '' }}</td>
<td>{{ $record['Current_Before'] ?? '' }}</td>
<td>{{ $record['Power_Before'] ?? '' }}</td>
<td>{{ $record['Resistance_RY'] ?? '' }}</td>
<td>{{ $record['Resistance_YB'] ?? '' }}</td>
<td>{{ $record['Resistance_BR'] ?? '' }}</td>
<td>{{ $record['Insulation_BBR'] ?? '' }}</td>
<td>{{ $record['Frequency_Before'] ?? '' }}</td>
<td>{{ $record['Speed_Before'] ?? '' }}</td>
{{-- AFTER FREE RUN --}}
<td>{{ $record['Voltage_After'] ?? '' }}</td>
<td>{{ $record['Current_After'] ?? '' }}</td>
<td>{{ $record['Power_After'] ?? '' }}</td>
<td>{{ $record['IR_Hot'] ?? '' }}</td>
<td>{{ $record['IR_Cool'] ?? '' }}</td>
<td>{{ $record['Frequency_After'] ?? '' }}</td>
<td>{{ $record['Speed_After'] ?? '' }}</td>
<td>{{ $record['Leakage_Current'] ?? '' }}</td>
{{-- LOCKED ROTOR TEST --}}
<td>{{ $record['Voltage_Locked'] ?? '' }}</td>
<td>{{ $record['Current_Locked'] ?? '' }}</td>
<td>{{ $record['Power_Locked'] ?? '' }}</td>
{{-- Other Info --}}
<td>{{ $record['No_Load_Pickup_Voltage'] ?? '' }}</td>
<td>{{ $record['Room_Temp'] ?? '' }}</td>
<td style="white-space: nowrap;">{{ $record['High_Voltage_Test'] ?? '' }}</td>
<td>{{ $record['Batch_Number'] ?? '' }}</td>
<td>{{ $record['Batch_Count'] ?? '' }}</td>
<td>{{ $record['Result'] ?? '' }}</td>
<td>{{ $record['Remark'] ?? '' }}</td>
<td>{{ $record['Tested_By'] ?? '' }}</td>
</tr>
@endforeach
</table>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,888 @@
{{-- <div>
<video id="video" width="320" height="240" autoplay playsinline style="border:1px solid #ccc;"></video>
<br>
<button type="button" id="captureBtn" class="mt-2 px-4 py-2 bg-blue-600 text-white rounded">Capture</button>
<canvas id="canvas" width="320" height="240" style="display:none;"></canvas>
<img id="snapshot" style="margin-top:10px; max-width:100%;">
<input type="hidden" id="camera_image" name="{{ $getName() }}">
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const video = document.getElementById('video');
const canvas = document.getElementById('canvas');
const captureBtn = document.getElementById('captureBtn');
const snapshot = document.getElementById('snapshot');
const cameraInput = document.getElementById('camera_image');
async function startCamera() {
try {
const stream = await navigator.mediaDevices.getUserMedia({
video: { facingMode: "user" } // front camera
});
video.srcObject = stream;
} catch (err) {
console.error("Camera error: ", err);
alert("Cannot access camera. Check permissions or HTTPS.");
}
}
captureBtn.addEventListener('click', () => {
const context = canvas.getContext('2d');
context.drawImage(video, 0, 0, canvas.width, canvas.height);
const dataUrl = canvas.toDataURL('image/png');
snapshot.src = dataUrl;
cameraInput.value = dataUrl;
});
startCamera();
});
</script> --}}
{{-- <div x-data="cameraCapture()" x-init="initCamera()" class="space-y-2" wire:ignore class="space-y-2">
<video x-ref="video" width="320" height="240" autoplay playsinline class="border rounded"></video>
<canvas x-ref="canvas" width="320" height="240" class="hidden"></canvas>
<img x-ref="snapshot" class="hidden border rounded max-w-full"> --}}
{{-- <div x-data="cameraCapture()" x-init="initCamera()" wire:ignore class="space-y-2">
<video
x-ref="video"
autoplay
playsinline
class="border rounded w-80 h-auto"
></video>
<!-- no need to fix width/height here either -->
<canvas x-ref="canvas" class="hidden"></canvas>
<img x-ref="snapshot" class="hidden border rounded max-w-full"> --}}
{{--
<div class="flex space-x-8 mt-2">
<x-filament::button color="primary" @click="capturePhoto" x-show="!photoTaken">Capture</x-filament::button>
<x-filament::button color="primary" @click="retakePhoto" x-show="photoTaken">Retake</x-filament::button>
<x-filament::button color="primary" @click="switchCamera" x-show="!photoTaken">Switch Camera</x-filament::button>
<x-filament::button color="primary" @click="verify" x-show="photoTaken">Verify</x-filament::button>
</div> --}}
{{-- <div class="flex space-x-2 mt-2">
<x-filament::button color="primary" @click="capturePhoto" x-show="!photoTaken" class="inline-flex w-auto">Capture</x-filament::button>
<x-filament::button color="primary" @click="retakePhoto" x-show="photoTaken" class="inline-flex w-auto">Retake</x-filament::button>
<x-filament::button color="primary" @click="switchCamera" x-show="!photoTaken" class="inline-flex w-auto">Switch Camera</x-filament::button>
<x-filament::button color="primary" @click="verify" x-show="photoTaken" class="inline-flex w-auto">Verify</x-filament::button>
</div> --}}
{{-- <input type="hidden" name="{{ $getName() }}" x-ref="hiddenInput"> --}}
{{-- <input type="hidden" x-ref="hiddenInput" name="camera_capture"> --}}
{{-- <input type="hidden" x-ref="hiddenInput" id="camera_capture_field" name="camera_capture_file">
</div>
<script src="https://cdn.jsdelivr.net/npm/tesseract.js@4.1.2/dist/tesseract.min.js"></script>
<script>
// function cameraCapture()
// {
// return
// {
// stream: null,
// currentFacingMode: 'user', // 'user' = front, 'environment' = back
// photoTaken: false,
// photo1: '',
// async initCamera() {
// try {
// if (this.stream) {
// this.stream.getTracks().forEach(track => track.stop());
// }
// this.stream = await navigator.mediaDevices.getUserMedia({
// video: { facingMode: this.currentFacingMode }
// });
// this.$refs.video.srcObject = this.stream;
// } catch (err) {
// console.error("Camera error:", err);
// alert("Cannot access camera. Enable permissions or use HTTPS.");
// }
// },
// async switchCamera() {
// this.currentFacingMode = this.currentFacingMode === 'user' ? 'environment' : 'user';
// await this.initCamera();
// },
// capturePhoto() {
// const video = this.$refs.video;
// const canvas = this.$refs.canvas;
// const snapshot = this.$refs.snapshot;
// const context = canvas.getContext('2d');
// context.drawImage(video, 0, 0, canvas.width, canvas.height);
// const dataUrl = canvas.toDataURL('image/png');
// // stop camera stream after capture
// if (this.stream) {
// this.stream.getTracks().forEach(track => track.stop());
// }
// snapshot.src = dataUrl;
// snapshot.classList.remove('hidden');
// video.classList.add('hidden');
// this.photoTaken = true;
// // this.photo1 = dataUrl;
// this.$refs.hiddenInput.value = dataUrl;
// // @this.set('photo1', dataUrl);
// console.log('Captured Image:', dataUrl);
// },
// async verifyOCR(dataUrl) {
// try {
// const { data: { text } } = await Tesseract.recognize(
// dataUrl,
// 'eng', // language
// { logger: m => console.log(m) } // optional
// );
// alert("OCR Result: " + text);
// } catch (err) {
// console.error(err);
// alert("OCR Failed: " + err.message);
// }
// },
// async verify() {
// const dataUrl = this.$refs.hiddenInput.value;
// if (!dataUrl) {
// alert("No captured image found!");
// return;
// }
// await this.verifyOCR(dataUrl);
// },
// async retakePhoto() {
// this.photoTaken = false;
// this.$refs.snapshot.classList.add('hidden');
// this.$refs.video.classList.remove('hidden');
// await this.initCamera();
// }
// }
// }
function cameraCapture() {
return {
stream: null,
currentFacingMode: 'user',
photoTaken: false,
photo1: '',
async initCamera() {
try {
if (this.stream) this.stream.getTracks().forEach(track => track.stop());
this.stream = await navigator.mediaDevices.getUserMedia({
video: { facingMode: this.currentFacingMode }
});
this.$refs.video.srcObject = this.stream;
} catch (err) {
console.error("Camera error:", err);
alert("Cannot access camera. Enable permissions or use HTTPS.");
}
},
async switchCamera() {
this.currentFacingMode = this.currentFacingMode === 'user' ? 'environment' : 'user';
await this.initCamera();
},
capturePhoto() {
const video = this.$refs.video;
const canvas = this.$refs.canvas;
const snapshot = this.$refs.snapshot;
const context = canvas.getContext('2d');
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
context.drawImage(video, 0, 0, canvas.width, canvas.height);
//const dataUrl = canvas.toDataURL('image/png');
const dataUrl = canvas.toDataURL('image/jpeg', 0.95);
if (this.stream) this.stream.getTracks().forEach(track => track.stop());
snapshot.src = dataUrl;
snapshot.classList.remove('hidden');
video.classList.add('hidden');
this.photoTaken = true;
this.$refs.hiddenInput.value = dataUrl;
console.log('Captured Image:', dataUrl);
},
async verifyOCR(dataUrl) {
try {
const { data: { text } } = await Tesseract.recognize(
dataUrl,
'eng',
{ logger: m => console.log(m) }
);
alert("OCR Result: " + text);
} catch (err) {
console.error(err);
alert("OCR Failed: " + err.message);
}
}, // <-- COMMA ADDED HERE
async verify() {
const dataUrl = this.$refs.hiddenInput.value;
if (!dataUrl) {
alert("No captured image found!");
return;
}
await this.verifyOCR(dataUrl);
},
async retakePhoto() {
this.photoTaken = false;
this.$refs.snapshot.classList.add('hidden');
this.$refs.video.classList.remove('hidden');
await this.initCamera();
}
}
}
</script> --}}
{{-- //..Another Option --}}
<div x-data="cameraCapture()" x-init="initCamera()" wire:ignore class="space-y-2">
<video
x-ref="video"
autoplay
playsinline
class="border rounded w-80 h-auto"
></video>
<!-- OCR Highlight Layer -->
<canvas
x-ref="overlay"
class="border rounded w-80 h-auto"
style="position:absolute; top:0; left:0; pointer-events:none;"
></canvas>
<canvas x-ref="canvas" class="hidden"></canvas>
<img x-ref="snapshot" class="hidden border rounded max-w-full">
{{-- <img x-ref="snapshot"
class="hidden border rounded"
style="width: 100%; max-width: 350px; height: auto;"> --}}
<div class="flex space-x-4 mt-2">
<x-filament::button color="primary" @click="capturePhoto" x-show="!photoTaken">Capture</x-filament::button>
<x-filament::button color="primary" @click="retakePhoto" x-show="photoTaken">Retake</x-filament::button>
<x-filament::button color="primary" @click="switchCamera" x-show="!photoTaken" class="inline-flex w-auto">Switch Camera</x-filament::button>
<x-filament::button color="primary" @click="verify" x-show="photoTaken" class="inline-flex w-auto">Verify</x-filament::button>
<x-filament::button color="success" @click="uploadCroppedImage" x-show="photoTaken">OK Upload Cropped</x-filament::button>
<x-filament::button color="success" @click="uploadOcr" x-show="photoTaken">Upload OCR</x-filament::button>
</div>
<input type="hidden" x-ref="hiddenInput" x-model="photo1" name="camera_capture_file">
<input type="hidden" x-ref="serialInput" name="serialNumbers">
</div>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.13/cropper.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.13/cropper.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/tesseract.js@4.1.3/dist/tesseract.min.js"></script>
<script>
function cameraCapture() {
return {
stream: null,
currentFacingMode: 'user',
photoTaken: false,
photo1: '',
async initCamera() {
try {
if (this.stream) this.stream.getTracks().forEach(track => track.stop());
this.stream = await navigator.mediaDevices.getUserMedia({
video: { facingMode: this.currentFacingMode }
});
this.$refs.video.srcObject = this.stream;
//this.startDetection();
} catch (err) {
console.error("Camera error:", err);
alert("Cannot access camera. Enable permissions or use HTTPS.");
}
},
async switchCamera() {
this.currentFacingMode = this.currentFacingMode === 'user' ? 'environment' : 'user';
await this.initCamera();
},
async capturePhoto() {
const video = this.$refs.video;
const canvas = this.$refs.canvas;
const ctx = canvas.getContext('2d');
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
ctx.drawImage(video, 0, 0);
const snapshot = this.$refs.snapshot;
snapshot.src = canvas.toDataURL('image/png');
//Wait until image is loaded
snapshot.onload = () => {
snapshot.classList.remove('hidden');
video.classList.add('hidden');
//Alpine reactive update inside nextTick
this.$nextTick(() => {
this.photoTaken = true;
//Destroy old cropper if exists
if (this.cropper) this.cropper.destroy();
// ✅ Use requestAnimationFrame to ensure browser painted the image
requestAnimationFrame(() => {
this.cropper = new Cropper(snapshot, {
aspectRatio: NaN,
dragMode: 'crop',
viewMode: 1,
autoCropArea: 0.8,
background: true,
movable: true,
zoomable: true,
responsive: true,
});
console.log("✅ Cropper initialized");
});
this.stopCamera(); // stop camera after Cropper starts
});
};
},
//
async uploadCroppedImage() {
if (!this.cropper) {
alert("Crop the image before upload!");
return;
}
const croppedCanvas = this.cropper.getCroppedCanvas({ imageSmoothingEnabled: true });
croppedCanvas.toBlob(async blob => {
const formData = new FormData();
formData.append('photo', blob, 'cropped.png');
const response = await fetch('/temp-upload', {
method: 'POST',
headers: { 'X-CSRF-TOKEN': '{{ csrf_token() }}' },
body: formData
});
const data = await response.json();
if (data.success) {
this.$refs.hiddenInput.value = data.path;
alert("✅ Cropped image uploaded!");
} else {
alert("Upload failed!");
}
}, "image/png");
},
async verify() {
const filePath = this.$refs.hiddenInput.value; // e.g., "temp/capture_1760764396.jpeg"
if (!filePath) {
alert("No captured image found!");
return;
}
try {
const response = await fetch('/verify-ocr', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': '{{ csrf_token() }}'
},
body: JSON.stringify({ path: filePath })
});
const data = await response.json();
console.log(data);
// if (data.success) {
// alert("OCR Result: " + data.text);
// console.error(data.text);
// }
if (data.success) {
// const serials = Array.isArray(data.text) ? data.text.join("\n") : data.text;
// alert("OCR Result:\n" + serials);
// console.log(serials);
const serials = Array.isArray(data.text) ? data.text : [data.text];
const firstFour = serials.slice(0, 4);
// Emit Livewire event to Resource Page
// window.dispatchEvent(new CustomEvent('set-serial-numbers', {
// detail: { serialNumbers: firstFour }
// }));
// Fill hidden input for Filament action
this.$refs.serialInput.value = JSON.stringify(firstFour);
alert("OCR Result:\n" + firstFour.join("\n"));
console.log("Serials sent to Resource Page:", firstFour);
}
else {
alert("OCR Failed: " + data.error);
console.error(data.error);
}
} catch (err) {
console.error(err.message);
alert("OCR request failed: " + err.message);
}
},
async retakePhoto() {
this.photoTaken = false;
this.$refs.snapshot.classList.add('hidden');
this.$refs.video.classList.remove('hidden');
this.cropper?.destroy();
await this.initCamera();
}
}
}
</script>
{{-- //.. --}}
{{-- <div x-data="cameraCapture()" x-init="initCamera()" wire:ignore class="relative space-y-2">
<!-- Video feed -->
<video
x-ref="video"
autoplay
playsinline
class="border rounded w-80 h-auto"
style="display:block;"
></video>
<!-- Overlay canvas for OCR highlight -->
<canvas
x-ref="overlay"
class="border rounded w-80 h-auto"
style="position:absolute; top:0; left:0; pointer-events:none;"
></canvas>
<!-- Hidden canvas for capturing snapshot if needed -->
<canvas x-ref="canvas" class="hidden"></canvas>
<div class="flex space-x-4 mt-2">
<x-filament::button color="primary" @click="switchCamera">Switch Camera</x-filament::button>
<x-filament::button color="success" @click="capturePhoto">Capture Photo</x-filament::button>
</div>
<input type="hidden" x-ref="hiddenInput" name="camera_capture_file">
</div> --}}
<!-- Scripts -->
{{-- <script src="https://cdn.jsdelivr.net/npm/tesseract.js@2.1.5/dist/tesseract.min.js"></script>
<script>
function cameraCapture() {
return {
stream: null,
currentFacingMode: 'user',
textDetectionInterval: null,
async initCamera() {
try {
// Stop any existing streams
if (this.stream) this.stream.getTracks().forEach(track => track.stop());
const video = this.$refs.video;
this.stream = await navigator.mediaDevices.getUserMedia({
video: { facingMode: this.currentFacingMode }
});
video.srcObject = this.stream;
// Wait for metadata to load
await new Promise(resolve => video.onloadedmetadata = resolve);
video.play();
// Overlay size matches video
const overlay = this.$refs.overlay;
overlay.width = video.videoWidth;
overlay.height = video.videoHeight;
// Start OCR detection
this.startDetection();
} catch (err) {
console.error("Camera error:", err);
alert("Camera error:\n" + (err.message || err));
}
},
async switchCamera() {
this.currentFacingMode = this.currentFacingMode === 'user' ? 'environment' : 'user';
await this.initCamera();
},
async capturePhoto() {
const video = this.$refs.video;
const canvas = this.$refs.canvas;
const ctx = canvas.getContext('2d');
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
ctx.drawImage(video, 0, 0);
// Save captured image to hidden input (optional)
const snapshot = canvas.toDataURL('image/png');
this.$refs.hiddenInput.value = snapshot;
alert("Photo captured!");
},
async detectText() {
const video = this.$refs.video;
const overlay = this.$refs.overlay;
const ctx = overlay.getContext('2d');
if (!video.videoWidth) return;
const tempCanvas = document.createElement('canvas');
tempCanvas.width = video.videoWidth;
tempCanvas.height = video.videoHeight;
const tempCtx = tempCanvas.getContext('2d');
tempCtx.drawImage(video, 0, 0);
try {
const result = await Tesseract.recognize(tempCanvas, 'eng', {
logger: m => console.log(m)
});
const words = result.data.words;
ctx.clearRect(0, 0, overlay.width, overlay.height);
ctx.strokeStyle = 'lime';
ctx.lineWidth = 2;
words.forEach(w => {
if (!w.bbox || w.confidence < 50) return;
const { x0, y0, x1, y1 } = w.bbox;
ctx.strokeRect(x0, y0, x1 - x0, y1 - y0);
});
} catch (err) {
console.error("OCR error:", err);
}
},
startDetection() {
if (this.textDetectionInterval) clearInterval(this.textDetectionInterval);
this.textDetectionInterval = setInterval(() => this.detectText(), 1000);
}
}
}
</script> --}}
{{-- .. --}}
{{-- <div x-data="cameraCapture()" x-init="initCamera()" wire:ignore class="relative space-y-2">
<!-- Video feed -->
<video
x-ref="video"
autoplay
playsinline
class="border rounded w-80 h-auto"
style="display:block;"
></video>
<!-- Image Preview -->
{{-- <img x-ref="snapshot" class="border rounded w-80 h-auto hidden" /> --}}
{{-- <img
x-ref="snapshot"
class="absolute top-0 left-0 w-full h-full border rounded hidden"
/> --}}
<!-- Overlay canvas for OCR highlight -->
{{-- <canvas
x-ref="overlay"
class="border rounded w-80 h-auto"
style="position:absolute; top:0; left:0; pointer-events:none;"
></canvas> --}}
<!-- Hidden canvas for capturing snapshot -->
{{-- <canvas x-ref="canvas" class="hidden"></canvas>
<div class="flex space-x-4 mt-2">
<x-filament::button color="primary" @click="switchCamera">Switch Camera</x-filament::button>
<x-filament::button color="success" @click="capturePhoto">Capture Photo</x-filament::button>
<x-filament::button color="warning" @click="verifyPhoto">Verify</x-filament::button>
<x-filament::button color="primary" @click="retakePhoto">Retake</x-filament::button>
</div>
<input type="hidden" x-ref="hiddenInput" name="camera_capture_file"> --}}
{{-- <input type="hidden" x-ref="serialInput" name="serialNumbers"> --}}
{{-- <input type="hidden" x-model="serialNumbers" name="serialNumbers"> --}}
{{-- <input type="hidden" x-model="serialNumbers" name="serialNumbers">
<input type="hidden" x-ref="hiddenInputSerials" name="serial_numbers">
</div> --}}
<!-- Scripts -->
{{-- <script src="https://cdn.jsdelivr.net/npm/tesseract.js@2.1.5/dist/tesseract.min.js"></script> --}}
{{--
<script>
function cameraCapture() {
return {
stream: null,
currentFacingMode: 'user',
textDetectionInterval: null,
capturedPhoto: null, // store captured image
serialNumbers: [],
async initCamera() {
try {
if (this.stream) this.stream.getTracks().forEach(track => track.stop());
const video = this.$refs.video;
this.stream = await navigator.mediaDevices.getUserMedia({
video: { facingMode: this.currentFacingMode }
});
video.srcObject = this.stream;
await new Promise(resolve => video.onloadedmetadata = resolve);
video.play();
// Overlay size matches video
const overlay = this.$refs.overlay;
overlay.width = video.videoWidth;
overlay.height = video.videoHeight;
this.startDetection();
} catch (err) {
console.error("Camera error:", err);
alert("Camera error:\n" + (err.message || err));
this.stopDetection();
}
},
async switchCamera() {
this.currentFacingMode = this.currentFacingMode === 'user' ? 'environment' : 'user';
await this.initCamera();
},
// async capturePhoto() {
// const video = this.$refs.video;
// const canvas = this.$refs.canvas;
// const ctx = canvas.getContext('2d');
// canvas.width = video.videoWidth;
// canvas.height = video.videoHeight;
// ctx.drawImage(video, 0, 0);
// // const snapshotData = canvas.toDataURL('image/png');
// // this.$refs.hiddenInput.value = snapshotData;
// // this.capturedPhoto = snapshotData; // store for verification
// const snapshotData = canvas.toDataURL('image/png');
// this.$refs.hiddenInput.value = snapshotData;
// this.capturedPhoto = snapshotData;
// // Stop camera stream
// if (this.stream) this.stream.getTracks().forEach(track => track.stop());
// // snapshot.src = dataUrl;
// // snapshot.classList.remove('hidden');
// // video.classList.add('hidden');
// // const snapshot = this.$refs.snapshot;
// // snapshot.src = snapshotData;
// // snapshot.classList.remove('hidden');
// // video.classList.add('hidden');
// // overlay.classList.add('hidden');
// snapshot.src = dataUrl;
// snapshot.classList.remove('hidden');
// video.classList.add('hidden');
// alert("Photo captured!");
// this.stopDetection();
// },
async capturePhoto() {
const video = this.$refs.video;
const canvas = this.$refs.canvas;
const overlay = this.$refs.overlay;
const snapshot = this.$refs.snapshot; // ✅ Fix: define snapshot reference
const ctx = canvas.getContext('2d');
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
ctx.drawImage(video, 0, 0);
const snapshotData = canvas.toDataURL('image/png'); // ✅ Correct data var
this.$refs.hiddenInput.value = snapshotData;
this.capturedPhoto = snapshotData;
// ✅ Stop camera
if (this.stream) this.stream.getTracks().forEach(track => track.stop());
// ✅ Hide video + overlay
video.classList.add('hidden');
overlay.classList.add('hidden');
// ✅ Show captured image
snapshot.src = snapshotData; // ✅ Correct variable
snapshot.classList.remove('hidden');
alert("Photo captured!");
this.stopDetection();
},
async verifyPhoto() {
if (!this.capturedPhoto) {
alert("Please capture a photo first!");
return;
}
try {
const img = new Image();
img.src = this.capturedPhoto;
img.onload = async () => {
const canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
const result = await Tesseract.recognize(canvas, 'eng', {
logger: m => console.log(m)
});
const detectedText = result.data.text.trim();
alert("Detected Text:\n" + (detectedText || "[No text detected]"));
// Extract serial numbers (digits only)
const matches = detectedText.match(/\d+/g) || [];
this.serialNumbers = matches.slice(0, 4); // take first 4 serials
this.$refs.hiddenInputSerials.value = JSON.stringify(this.serialNumbers);
//this.$refs.serialInput.value = JSON.stringify(this.serialNumbers);
alert("Serial numbers stored in hidden input:\n" + this.$refs.serialInput.value);
// ✅ Save to Laravel Session using POST API
fetch('/save-serials-to-session', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content,
},
body: JSON.stringify({
serial_numbers: this.serialNumbers,
}),
})
.then(response => response.json())
.then(data => {
console.log("✅ Session Updated:", data);
alert("✅ Serial numbers saved to session!");
})
}
} catch (err) {
console.error("OCR verify error:", err);
alert("OCR verify failed:\n" + (err.message || err));
}
},
async detectText() {
const video = this.$refs.video;
const overlay = this.$refs.overlay;
const ctx = overlay.getContext('2d');
if (!video.videoWidth) return;
const tempCanvas = document.createElement('canvas');
tempCanvas.width = video.videoWidth;
tempCanvas.height = video.videoHeight;
const tempCtx = tempCanvas.getContext('2d');
tempCtx.drawImage(video, 0, 0);
try {
const result = await Tesseract.recognize(tempCanvas, 'eng');
const words = result.data.words;
ctx.clearRect(0, 0, overlay.width, overlay.height);
ctx.strokeStyle = 'lime';
ctx.lineWidth = 2;
words.forEach(w => {
if (!w.bbox || w.confidence < 50) return;
const { x0, y0, x1, y1 } = w.bbox;
ctx.strokeRect(x0, y0, x1 - x0, y1 - y0);
});
} catch (err) {
console.error("Live OCR error:", err);
}
},
async retakePhoto() {
this.photoTaken = false;
this.$refs.snapshot.classList.add('hidden');
this.$refs.video.classList.remove('hidden');
await this.initCamera();
this.startDetection();
},
startDetection() {
if (this.textDetectionInterval) clearInterval(this.textDetectionInterval);
this.textDetectionInterval = setInterval(() => this.detectText(), 1000);
},
stopDetection() {
if (this.textDetectionInterval) {
clearInterval(this.textDetectionInterval);
this.textDetectionInterval = null;
console.log("Text detection stopped");
}
}
}
}
</script> --}}

View File

@@ -0,0 +1,3 @@
<x-filament-panels::page>
</x-filament-panels::page>

View File

@@ -0,0 +1,21 @@
<x-filament-panels::page>
{{ $this->form }}
<style>
.align-to-input {
align-self: center;
margin-top: 8mm; /* or 0.3rem */
}
</style>
<!-- Label for Quality Data Table -->
{{-- <div class="mb-4">
<h2 class="text-xl font-semibold text-gray-800">Quality Data Table</h2>
</div> --}}
<div class="bg-white shadow rounded-xl p-4 mt-6">
<livewire:sap-data />
</div>
</x-filament-panels::page>

View File

@@ -0,0 +1,11 @@
<x-filament-panels::page>
<div class="space-y-4">
{{-- Filters form --}}
{{ $this->filtersForm($this->form) }}
{{-- Chart widget --}}
<x-filament-widgets::widgets :widgets="$this->getWidgets()" />
</div>
</x-filament-panels::page>

View File

@@ -0,0 +1,15 @@
<x-filament-panels::page>
<div class="space-y-4">
{{-- Render the Select form fields --}}
<div class="space-y-4">
{{ $this->filtersForm($this->form) }}
</div>
<div class="bg-white shadow rounded-xl p-4 mt-6">
<livewire:guard-patrol-entry-data-table />
{{-- @livewire(\App\Filament\Widgets\InvoiceChart::class) --}}
</div>
</div>
</x-filament-panels::page>

View File

@@ -0,0 +1,10 @@
<x-filament-panels::page>
<div class="space-y-4">
{{-- Filters form --}}
{{ $this->filtersForm($this->form) }}
{{-- Chart widget --}}
<x-filament-widgets::widgets :widgets="$this->getWidgets()" />
</div>
</x-filament-panels::page>

View File

@@ -0,0 +1,38 @@
{{-- <x-filament-panels::page>
<div class="space-y-4"> --}}
{{-- Filters form --}}
{{-- {{ $this->filtersForm($this->form) }} --}}
{{-- Chart widget --}}
{{-- <x-filament-widgets::widgets :widgets="$this->getWidgets()" /> --}}
{{-- <div class="col-span-2"> --}}
{{-- Stat widget Livewire --}}
{{-- @livewire(\App\Filament\Widgets\ProductionQuantityStat::class)
</div>
</div>
</x-filament-panels::page> --}}
<x-filament-panels::page>
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-2 sm:gap-4 md:gap-4 p-2 md:p-4 items-start">
{{-- Filters form --}}
<div class="space-y-4 w-full max-w-full col-span-1">
{{ $this->filtersForm($this->form) }}
</div>
{{-- Stat widget --}}
<div class="w-full max-w-full col-span-1 sm:col-span-1 lg:col-span-2">
{{-- @livewire(\App\Filament\Widgets\ProductionQuantityStat::class) --}}
@livewire(\App\Filament\Widgets\ItemOverview::class)
</div>
</div>
</x-filament-panels::page>

View File

@@ -0,0 +1,19 @@
<x-filament-panels::page>
<div class="space-y-4">
{{-- Render the Select form fields --}}
<div class="space-y-4">
{{ $this->filtersForm($this->form) }}
</div>
{{-- Render the chart widget below the form --}}
<div class="mt-6">
@livewire(\App\Filament\Widgets\InvoiceChart::class)
</div>
</div>
</x-filament-panels::page>

View File

@@ -0,0 +1,13 @@
<x-filament-panels::page>
<div class="space-y-4">
{{-- Render the Select form fields --}}
<div class="space-y-4">
{{ $this->filtersForm($this->form) }}
</div>
{{-- Render the chart widget below the form --}}
<div class="mt-6">
@livewire(\App\Filament\Widgets\InvoiceDataChart::class)
</div>
</div>
</x-filament-panels::page>

View File

@@ -0,0 +1,14 @@
<x-filament-panels::page>
<div class="space-y-4">
{{-- Render the Select form fields --}}
<div class="space-y-4">
{{ $this->form }}
</div>
<div class="bg-white shadow rounded-xl p-4 mt-6">
<livewire:invoice-finder-data-table />
</div>
</div>
</x-filament-panels::page>

View File

@@ -0,0 +1,13 @@
<x-filament-panels::page>
<div class="space-y-4">
{{-- Render the Select form fields --}}
<div class="space-y-4">
{{ $this->filtersForm($this->form) }}
</div>
{{-- Render the chart widget below the form --}}
<div class="mt-6">
@livewire(\App\Filament\Widgets\InvoiceQuantity::class)
</div>
</div>
</x-filament-panels::page>

View File

@@ -0,0 +1,14 @@
<x-filament-panels::page>
<div class="space-y-4">
{{-- Render the Select form fields --}}
<div class="space-y-4">
{{ $this->form }}
</div>
<div class="bg-white shadow rounded-xl p-4 mt-6">
<livewire:invoice-rework-data-table />
</div>
</div>
</x-filament-panels::page>

View File

@@ -0,0 +1,77 @@
{{-- <x-filament-panels::page>
<div class="space-y-4">
<div class="space-y-4">
{{ $this->form }}
</div>
<div class="bg-white shadow rounded-xl p-4 mt-6">
<livewire:locator-data-table />
</div>
</div>
<script>
window.addEventListener('focus-scan-locator-no', () => {
const wrapper = document.getElementById('scan_locator_no');
const input = wrapper?.querySelector('input,textarea');
if (input) {
input.focus();
input.select();
}
});
</script>
</x-filament-panels::page> --}}
<x-filament-panels::page>
<div class="space-y-4">
{{-- Render the Select form fields --}}
<div class="space-y-4">
{{ $this->form }}
</div>
{{-- Add Pallet and Remove Pallet buttons --}}
<div class="flex flex-row gap-2 mt-4">
<button
type="button"
wire:click="addPallet"
class="px-3 py-1 border border-primary-500 text-primary-600 rounded hover:bg-primary-50 hover:border-primary-700 transition text-sm"
>
Add Pallet /<br>Serial Number
</button>
<button
type="button"
wire:click="removePallet"
class="px-3 py-1 border border-primary-500 text-primary-600 rounded hover:bg-primary-50 hover:border-primary-700 transition text-sm"
>
Remove Pallet /<br>Serial Number
</button>
{{-- <button
type="button"
wire:click="print"
class="px-3 py-1 border border-primary-500 text-primary-600 rounded hover:bg-primary-50 hover:border-primary-700 transition text-sm"
>
Print
</button> --}}
</div>
<div class="bg-white shadow rounded-xl p-4 mt-6">
<livewire:locator-data-table />
</div>
</div>
<script>
window.addEventListener('focus-scan-locator-no', () => {
const wrapper = document.getElementById('scan_locator_no');
const input = wrapper?.querySelector('input,textarea');
if (input) {
input.focus();
input.select();
}
});
</script>
</x-filament-panels::page>

View File

@@ -0,0 +1,51 @@
<x-filament-panels::page>
<div class="space-y-4">
{{-- Render the Select form fields --}}
<div class="space-y-4">
{{ $this->form }}
</div>
<div class="flex-row gap-2 mt-4">
<button
type="button"
wire:click="generatePallet"
class="px-3 py-1 border border-primary-500 text-primary-600 rounded hover:bg-primary-50 hover:border-primary-700 transition text-sm"
>
Generate Pallet
</button>
</div>
<div class="bg-white shadow rounded-xl p-4 mt-6">
<livewire:pallet-from-locator-data-table />
</div>
{{-- <div>
<p>Locator No: {{ $locatorNo }}</p>
</div> --}}
<x-filament::modal id="confirm-process-modal">
<x-slot name="heading">
ADD: CONFIRMATION
</x-slot>
<p>Scanned locator number has locator serial numbers!<br>Do you want to store it into 'Pallet Data' table?</p>
<x-slot name="footer">
<x-filament::button wire:click="addToPalletValidation" x-on:click="isOpen = false" color="success">
Yes
</x-filament::button>
<x-filament::button wire:click="skipAddToPalletValidation" x-on:click="isOpen = false" color="danger">
No
</x-filament::button>
</x-slot>
</x-filament::modal>
@push('scripts')
<script>
window.addEventListener('open-pdf', event => {
const url = event.detail.url;
const win = window.open(url, '_blank');
if (!win || win.closed || typeof win.closed == 'undefined') {
alert('Popup blocked. Please allow popups for this site.');
}
});
</script>
@endpush
</div>
</x-filament-panels::page>

View File

@@ -0,0 +1,22 @@
<x-filament-panels::page>
{{ $this->form }}
<style>
.align-to-input {
align-self: center;
margin-top: 8mm; /* or 0.3rem */
}
</style>
<!-- Label for Quality Data Table -->
{{-- <div class="mb-4">
<h2 class="text-xl font-semibold text-gray-800">Quality Data Table</h2>
</div> --}}
<div class="bg-white shadow rounded-xl p-4 mt-6">
<livewire:production-sap-data />
</div>
</x-filament-panels::page>

View File

@@ -0,0 +1,13 @@
<x-filament-panels::page>
<div class="space-y-4">
{{-- Render the Select form fields --}}
<div class="space-y-4">
{{ $this->filtersForm($this->form) }}
</div>
{{-- Render the chart widget below the form --}}
<div class="mt-6">
@livewire(\App\Filament\Widgets\ProductionLineStopChart::class)
</div>
</div>
</x-filament-panels::page>

View File

@@ -0,0 +1,14 @@
<x-filament-panels::page>
<div class="space-y-4">
{{-- Render the Select form fields --}}
<div class="space-y-4">
{{ $this->filtersForm($this->form) }}
</div>
{{-- Render the chart widget below the form --}}
<div class="mt-6">
@livewire(\App\Filament\Widgets\ProductionOrderChart::class)
</div>
</div>
</x-filament-panels::page>

View File

@@ -0,0 +1,109 @@
<x-filament-panels::page>
<div class="space-y-4">
{{-- Render the Select form fields --}}
<div class="space-y-4">
{{-- {{ $this->filtersForm($this->form) }} --}}
{{ $this->form }}
</div>
<livewire:notification-sound />
{{-- <input
type="text"
id="qr-scan-input"
class="border border-gray-300 rounded px-4 py-2 text-sm w-full"
placeholder="Scan QR Code & Press Enter"
autocomplete="off"
autofocus
/> --}}
{{-- <div class="mb-4">
<label for="qr-scan-input" class="block text-sm font-medium text-gray-700 mb-2">
SCAN QR CODE
</label>
<input
type="text"
id="qr-scan-input"
class="border border-gray-300 rounded px-4 py-2 text-sm w-full"
placeholder="Scan QR Code & Press Enter"
autocomplete="off"
autofocus
/>
</div> --}}
{{-- <div class="mb-4">
<label for="qr-scan-input" class="block text-sm font-medium text-gray-700 mb-2">
SCAN QR CODE
</label>
<input
type="text"
id="qr-scan-input"
class="border border-gray-300 rounded px-4 py-2 text-sm w-1/2"
placeholder="Scan QR Code & Press Enter"
autocomplete="off"
autofocus
/>
</div> --}}
<div class="flex gap-6 -mt-6">
<!-- Scan QR Code -->
<div class="w-1/2">
<label for="qr-scan-input" class="block text-sm font-medium text-gray-700 mb-2">
SCAN QR CODE
</label>
<input
type="text"
id="qr-scan-input"
class="border border-gray-300 rounded px-4 py-2 text-sm w-full"
placeholder="Scan QR Code & Press Enter"
autocomplete="off"
autofocus
/>
</div>
<!-- Last Scanned QR -->
<div class="w-1/2">
<label for="recent-qr-input" class="block text-sm font-medium text-gray-700 mb-2">
LAST SCANNED QR
</label>
<input
type="text"
id="recent-qr-input"
class="border border-gray-300 rounded px-4 py-2 text-sm w-full bg-white-100 text-black"
readonly
wire:model="recent_qr"
/>
</div>
</div>
@push('scripts')
<script>
document.addEventListener('DOMContentLoaded', function () {
const scanInput = document.getElementById('qr-scan-input');
if (!scanInput) return;
scanInput.addEventListener('keydown', function (event) {
if (event.key === 'Enter') {
event.preventDefault();
const value = scanInput.value.trim();
if (value !== '') {
Livewire.dispatch('handleQrScan', { value: value });
scanInput.value = '';
}
}
});
});
</script>
@endpush
{{-- Render the chart widget below the form --}}
<div class="mt-6">
@livewire(\App\Filament\Widgets\ItemOverview::class)
</div>
</div>
</x-filament-panels::page>

View File

@@ -0,0 +1,73 @@
<x-filament-panels::page>
<div class="space-y-4">
{{-- Heading label --}}
{{-- <h2 class="text-xl font-semibold text-gray-800">
STICKER RE-PRINT
</h2> --}}
{{-- Render the Select form fields --}}
<div class="space-y-4">
{{-- {{ $this->filtersForm($this->form) }} --}}
{{ $this->form }}
</div>
{{-- <input
type="text"
id="qr-scan-input"
class="border border-gray-300 rounded px-4 py-2 text-sm w-full"
placeholder="Scan QR Code & Press Enter"
autocomplete="off"
autofocus
/> --}}
<div class="mb-4">
<label for="qr-scan-input" class="block text-sm font-medium text-gray-700 mb-2">
SCAN QR CODE
</label>
<input
type="text"
id="qr-scan-input"
class="border border-gray-300 rounded px-4 py-2 text-sm w-full"
placeholder="Scan QR Code & Press Enter"
autocomplete="off"
autofocus
/>
</div>
@push('scripts')
<script>
document.addEventListener('DOMContentLoaded', function () {
const scanInput = document.getElementById('qr-scan-input');
if (!scanInput) return;
scanInput.addEventListener('keydown', function (event) {
if (event.key === 'Enter') {
event.preventDefault();
const value = scanInput.value.trim();
if (value !== '') {
Livewire.dispatch('handleQrScan', { value: value });
scanInput.value = '';
}
}
});
window.addEventListener('open-pdf', event => {
const pdfUrl = event.detail.url;
const win = window.open(pdfUrl, '_blank');
if (!win) {
console.warn('Popup blocked. Please allow popups for this site.');
}
});
});
</script>
@endpush
{{-- Render the chart widget below the form --}}
<div class="mt-6">
@livewire(\App\Filament\Widgets\ItemOverview::class)
</div>
</div>
</x-filament-panels::page>

View File

@@ -0,0 +1,72 @@
<x-filament-panels::page>
<div class="space-y-4">
{{-- Heading label --}}
<h2 class="text-xl font-semibold text-gray-800">
STICKER RE-PRINT
</h2>
{{-- Render the Select form fields --}}
<div class="space-y-4">
{{-- {{ $this->filtersForm($this->form) }} --}}
{{ $this->form }}
</div>
{{-- <input
type="text"
id="qr-scan-input"
class="border border-gray-300 rounded px-4 py-2 text-sm w-full"
placeholder="Scan QR Code & Press Enter"
autocomplete="off"
autofocus
/> --}}
<div class="mb-4">
<label for="qr-scan-input" class="block text-sm font-medium text-gray-700 mb-2">
SCAN QR CODE
</label>
<input
type="text"
id="qr-scan-input"
class="border border-gray-300 rounded px-4 py-2 text-sm w-full"
placeholder="Scan QR Code & Press Enter"
autocomplete="off"
autofocus
/>
</div>
@push('scripts')
<script>
document.addEventListener('DOMContentLoaded', function () {
const scanInput = document.getElementById('qr-scan-input');
if (!scanInput) return;
scanInput.addEventListener('keydown', function (event) {
if (event.key === 'Enter') {
event.preventDefault();
const value = scanInput.value.trim();
if (value !== '') {
Livewire.dispatch('handleQrScan', { value: value });
scanInput.value = '';
}
}
});
window.addEventListener('open-pdf', event => {
const pdfUrl = event.detail.url;
const win = window.open(pdfUrl, '_blank');
if (!win) {
console.warn('Popup blocked. Please allow popups for this site.');
}
});
});
</script>
@endpush
{{-- Render the chart widget below the form --}}
<div class="mt-6">
@livewire(\App\Filament\Widgets\ItemOverview::class)
</div>
</div>
</x-filament-panels::page>

View File

@@ -0,0 +1,11 @@
<x-filament-panels::page>
<div class="space-y-4">
{{-- Filters form --}}
{{ $this->filtersForm($this->form) }}
{{-- Chart widget --}}
<x-filament-widgets::widgets :widgets="$this->getWidgets()" />
</div>
</x-filament-panels::page>

View File

@@ -0,0 +1,10 @@
<x-filament-panels::page>
<div class="space-y-4">
{{-- Filters form --}}
{{ $this->filtersForm($this->form) }}
{{-- Chart widget --}}
<x-filament-widgets::widgets :widgets="$this->getWidgets()" />
</div>
</x-filament-panels::page>

View File

@@ -0,0 +1,77 @@
<x-filament-panels::page>
<div class="space-y-4">
{{-- Render the Select form fields --}}
<div class="space-y-4">
{{ $this->form }}
</div>
<div class="flex-row gap-2 mt-4">
<button
type="button"
wire:click="masterFileUpload"
class="px-3 py-1 border border-primary-500 text-primary-600 rounded hover:bg-primary-50 hover:border-primary-700 transition text-sm"
>
Master File Upload
</button>
<button
type="button"
wire:click="addLocator"
class="px-3 py-1 border border-primary-500 text-primary-600 rounded hover:bg-primary-50 hover:border-primary-700 transition text-sm"
>
Add
</button>
<button
type="button"
wire:click="viewLocator"
class="px-3 py-1 border border-primary-500 text-primary-600 rounded hover:bg-primary-50 hover:border-primary-700 transition text-sm"
>
View
</button>
<button
type="button"
wire:click="deleteLocator"
class="px-3 py-1 border border-primary-500 text-primary-600 rounded hover:bg-primary-50 hover:border-primary-700 transition text-sm"
>
Delete
</button>
</div>
<div class="bg-white shadow rounded-xl p-4 mt-6">
<livewire:serial-locator-data-table />
</div>
<x-filament::modal id="confirm-process-modal">
<x-slot name="heading">
UPLOAD: CONFIRMATION
</x-slot>
<p>Some locator numbers does not have space?<br>Do you want to skip these locators?<br>Press Yes to continue!<br>Press No to Cancel!</p>
<x-slot name="footer">
<x-filament::button wire:click="skipLocatorsQuestion" x-on:click="isOpen = false" color="success">
Yes
</x-filament::button>
{{-- <x-filament::button x-on:click="isOpen = false" color="danger"> --}}
<x-filament::button wire:click="cancelLocatorsQuestion" x-on:click="isOpen = false" color="danger">
No
</x-filament::button>
</x-slot>
</x-filament::modal>
<x-filament::modal id="confirm-process-serial">
<x-slot name="heading">
UPLOAD: CONFIRMATION
</x-slot>
<p>Some Serial numbers are already exists in pallet table?<br>Do you want to skip the duplicate serial numbers?<br>Press Yes to continue!<br>Press No to Cancel!</p>
<x-slot name="footer">
<x-filament::button wire:click="skipSerialQuestion" x-on:click="isOpen = false" color="success">
Yes
</x-filament::button>
{{-- <x-filament::button x-on:click="isOpen = false" color="danger"> --}}
<x-filament::button wire:click="cancelSerialQuestion" x-on:click="isOpen = false" color="danger">
No
</x-filament::button>
</x-slot>
</x-filament::modal>
</div>
</x-filament-panels::page>

View File

@@ -0,0 +1,27 @@
<x-filament-panels::page>
{{-- <div class="space-y-4">
{{ $this->form($this->form) }}
</div> --}}
{{-- <div class="space-y-4">
{{ $this->form }} --}}
{{-- </div> --}}
{{-- Render the chart widget below the form --}}
{{-- <div class="mt-6">
@livewire(\App\Filament\Widgets\InvoiceChart::class)
</div> --}}
{{-- </div> --}}
<div class="space-y-4">
{{-- Render the Select form fields --}}
<div class="space-y-4">
{{ $this->filtersForm($this->form) }}
</div>
{{-- Render the chart widget below the form --}}
{{-- <div class="mt-6">
@livewire(\App\Filament\Widgets\InvoiceChart::class)
</div> --}}
</div>
</x-filament-panels::page>

View File

@@ -0,0 +1,25 @@
<x-filament::page>
<form wire:submit.prevent="create" class="space-y-6">
{{-- Form Section --}}
<div class="filament-form space-y-6">
{{ $this->form }}
</div>
<livewire:notification-sound />
{{-- Livewire Component (Invoice Table) --}}
<div class="bg-white shadow rounded-xl p-4">
<livewire:invoice-data-table :invoice-data="$invoice_data" />
</div>
{{-- Actions --}}
<div class="filament-actions mt-6">
<x-filament::actions>
@foreach ($this->getFormActions() as $action)
{{ $action }}
@endforeach
</x-filament::actions>
</div>
</form>
</x-filament::page>

View File

@@ -0,0 +1,21 @@
<x-filament::page>
<form wire:submit.prevent="create" class="space-y-6">
{{-- Form Section --}}
<div class="filament-form space-y-6">
{{ $this->form }}
</div>
{{-- Livewire Component (Invoice Table) --}}
<div class="bg-white shadow rounded-xl p-4">
<livewire:select-plant />
</div>
{{-- Actions --}}
<div class="filament-actions mt-6">
<x-filament::actions>
@foreach ($this->getFormActions() as $action)
{{ $action }}
@endforeach
</x-filament::actions>
</div>
</form>
</x-filament::page>

View File

@@ -0,0 +1,84 @@
{{-- <x-filament::page>
<form wire:submit.prevent="create" class="space-y-6">
{{-- Form Section --}}
{{-- <div class="filament-form space-y-6">
{{ $this->form }}
</div> --}}
{{-- Livewire Component (Invoice Table) --}}
{{-- <div class="bg-white shadow rounded-xl p-4">
<livewire:locator-invoice-data-table />
</div> --}}
{{-- Heading after Livewire component (optional) --}}
{{-- <h2>hello</h2> --}}
{{-- Actions --}}
{{-- <div class="filament-actions mt-6">
<x-filament::actions>
@foreach ($this->getFormActions() as $action)
{{ $action }}
@endforeach
</x-filament::actions>
</div>
</form>
</x-filament::page> --}}
<!-- Test button to manually trigger the modal -->
<x-filament::page>
<form wire:submit.prevent="create" class="space-y-6">
{{-- Form Section --}}
<div class="filament-form space-y-6">
{{ $this->form }}
</div>
{{-- Livewire Component (Invoice Table) --}}
<div class="bg-white shadow rounded-xl p-4">
<livewire:locator-invoice-data-table />
</div>
{{-- Actions --}}
<div class="filament-actions mt-6">
<x-filament::actions>
@foreach ($this->getFormActions() as $action)
{{ $action }}
@endforeach
</x-filament::actions>
</div>
</form>
{{-- Modal for removing matched serials --}}
<x-filament::modal wire:model="showRemoveSerialsModal">
<h1>hello</h1>
<x-slot name="title">
Remove Matched Serials from Pallet Validation?
</x-slot>
<x-slot name="content">
<div style="color: red; font-weight: bold;">
<!-- TEMPORARY INDICATOR -->
MODAL IS OPENED!
</div>
<div>
<p>The following serial numbers exist in both tables and will be removed from Pallet Validation if you proceed:</p>
<ul>
@foreach ($matchedSerialNumbersForRemoval as $serial)
<li>{{ $serial }}</li>
@endforeach
</ul>
</div>
</x-slot>
<x-slot name="footer">
<x-filament::button wire:click="removeMatchedSerials" color="danger">
Yes, Remove
</x-filament::button>
<x-filament::button wire:click="$set('showRemoveSerialsModal', false)">
No, Cancel
</x-filament::button>
</x-slot>
</x-filament::modal>
</x-filament::page>

View File

@@ -0,0 +1,46 @@
<x-filament::page>
<form wire:submit.prevent="create" class="space-y-6 mt-4"> <!-- Added mt-4 (16px) -->
<div class="filament-page-header space-y-2 mb-6">
{{-- Breadcrumbs --}}
<div class="filament-breadcrumbs flex items-center gap-2 text-sm">
<a href="{{ route('filament.admin.resources.pallet-validations.index') }}"
class="text-primary-500 hover:underline">
Pallet Validations
</a>
<span class="text-gray-500"> > </span>
<span class="text-gray-600">Create</span>
</div>
<h1 class="text-3xl font-bold tracking-tight">Scan Pallet</h1>
</div>
<div class="filament-form space-y-6">
{{ $this->form }}
</div>
<div class="bg-white shadow rounded-xl p-4">
<livewire:pallet-data-table />
</div>
<div class="filament-actions mt-6">
<x-filament::actions>
@foreach ($this->getFormActions() as $action)
{{ $action }}
@endforeach
</x-filament::actions>
</div>
</form>
@push('scripts')
<script>
window.addEventListener('open-pdf', event => {
const url = event.detail.url;
const win = window.open(url, '_blank');
if (!win || win.closed || typeof win.closed == 'undefined') {
alert('Popup blocked. Please allow popups for this site.');
}
});
</script>
@endpush
</x-filament::page>

View File

@@ -0,0 +1,28 @@
<x-filament::page>
<form wire:submit.prevent="create" class="space-y-6">
{{-- Form Section --}}
<div class="filament-form space-y-6">
{{ $this->form }}
</div>
{{-- Livewire Component (Invoice Table) --}}
<div class="bg-white shadow rounded-xl p-4">
<livewire:invoice-rework-data-table />
</div>
{{-- Heading after Livewire component (optional) --}}
{{-- <h2>hello</h2> --}}
{{-- Actions --}}
<div class="filament-actions mt-6">
<x-filament::actions>
@foreach ($this->getFormActions() as $action)
{{ $action }}
@endforeach
</x-filament::actions>
</div>
</form>
</x-filament::page>

View File

@@ -0,0 +1,24 @@
<x-filament::page>
<form wire:submit.prevent="create" class="space-y-6">
{{-- Form Section --}}
<div class="filament-form space-y-6">
{{ $this->form }}
</div>
<livewire:notification-sound />
{{-- Livewire Component (Invoice Table) --}}
<div class="bg-white shadow rounded-xl p-4">
<livewire:serial-validation-data :invoice-data="$invoice_data" />
</div>
{{-- Actions --}}
<div class="filament-actions mt-6">
<x-filament::actions>
@foreach ($this->getFormActions() as $action)
{{ $action }}
@endforeach
</x-filament::actions>
</div>
</form>
</x-filament::page>

View File

@@ -0,0 +1,24 @@
<x-filament::page>
<form wire:submit.prevent="create" class="space-y-6">
{{-- Form Section --}}
<div class="filament-form space-y-6">
{{ $this->form }}
</div>
{{-- <livewire:notification-sound /> --}}
{{-- Livewire Component (Invoice Table) --}}
<div class="bg-white shadow rounded-xl p-4">
<livewire:sticker-print-data :ref-data="$ref_number" />
</div>
{{-- Actions --}}
<div class="filament-actions mt-6">
<x-filament::actions>
@foreach ($this->getFormActions() as $action)
{{ $action }}
@endforeach
</x-filament::actions>
</div>
</form>
</x-filament::page>

View File

@@ -0,0 +1,23 @@
{{-- <div class="flex flex-col items-start space-y-2">
<button
type="button"
wire:click="printSticker"
class="mt-15 px-2 py-1 border border-primary-500 text-primary-600 rounded hover:bg-primary-50 hover:border-primary-700 transition text-sm"
>
Print
</button>
</div> --}}
<div class="flex flex-col items-start space-y-2">
<button
type="button"
wire:click="printSticker"
class="px-2 py-1 border border-primary-500 text-primary-600 bg-white rounded hover:bg-primary-50 hover:border-primary-700 transition text-sm"
style="margin-top: 10mm;"
>
Print
</button>
</div>

View File

@@ -0,0 +1,43 @@
<div class="flex flex-col items-start space-y-2">
<div class="flex items-center">
<input
type="checkbox"
id="is_completed"
wire:model.defer="data.is_completed"
class="focus:outline-none focus:ring-0 focus:border-transparent border-gray-300"
>
<label for="is_completed" style="margin-left:2mm;" class="whitespace-nowrap mb-0">
Is Completed!
</label>
</div>
<button
type="button"
wire:click="markAsComplete"
class="px-2 py-1 border border-primary-500 text-primary-600 rounded hover:bg-primary-50 hover:border-primary-700 transition text-sm"
>
Save Pallet
</button>
<!-- <div class="flex flex-col items-start space-y-2">
<div class="flex items-center space-x-8">
<input
type="checkbox"
id="is_completed"
wire:model.defer="data.is_completed"
class="focus:outline-none focus:ring-0 focus:border-transparent border-gray-300"
>
<label for="is_completed" class="whitespace-nowrap mb-0">
Is Completed!
</label>
<button
type="button"
wire:click="markAsComplete"
class="py-1 px-6 border border-primary-500 text-primary-600 rounded hover:bg-primary-50 hover:border-primary-700 transition text-sm whitespace-nowrap"
>
Save Pallet
</button>
</div>
</div> -->

View File

@@ -0,0 +1,121 @@
{{-- <link rel="manifest" href="{{ asset('manifest.json') }}">
<meta name="theme-color" content="#007bff">
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js')
.then(function(registration) {
console.log('Service Worker registered with scope:', registration.scope);
}).catch(function(err) {
console.log('Service Worker registration failed:', err);
});
}
</script> --}}
{{-- <!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ config('app.name', 'Laravel') }}</title>
<!-- PWA manifest and theme color -->
<link rel="manifest" href="{{ asset('manifest.json') }}">
<meta name="theme-color" content="#007bff">
<!-- Styles -->
<link href="{{ mix('css/app.css') }}" rel="stylesheet">
<!-- Service Worker -->
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/service-worker.js')
.then(function(registration) {
console.log('Service Worker registered with scope:', registration.scope);
}).catch(function(err) {
console.log('Service Worker registration failed:', err);
});
}
</script>
</head>
<body>
@yield('content')
</body>
</html> --}}
{{-- <!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>@yield('title', 'Weight Scale Validation')</title>
<!-- Laravel PWA manifest and meta tags -->
@PwaHead
<link href="{{ mix('css/app.css') }}" rel="stylesheet">
</head>
<body>
@yield('content')
<script src="{{ mix('js/app.js') }}"></script>
@RegisterServiceWorkerScript
</body>
</html> --}}
{{-- <!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>@yield('title', 'Weight Scale Validation')</title>
<!-- Laravel PWA manifest and meta tags -->
@laravelPWA
<link href="{{ mix('css/app.css') }}" rel="stylesheet">
</head>
<body>
@yield('content')
<script src="{{ mix('js/app.js') }}"></script>
</body>
</html> --}}
{{-- <!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>@yield('title', 'App')</title>
<!-- Add this -->
<link rel="manifest" href="{{ asset('manifest.json') }}">
<meta name="theme-color" content="#007bff">
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js')
.then(reg => console.log('Service Worker registered:', reg.scope))
.catch(err => console.error('Service Worker registration failed:', err));
}
</script>
</head>
<body>
@yield('content')
</body>
</html> --}}
<head>
<!-- other head content -->
@include('partials.pwa')
</head>

View File

@@ -0,0 +1,88 @@
<div class="p-4">
<h2 class="text-lg font-bold mb-4 text-gray-700 uppercase tracking-wider">
GUARD PATROL ENTRY STATUS TABLE :
</h2>
<div class="overflow-x-auto rounded-lg shadow">
<table class="w-full w-[600px] divide-y divide-gray-200 text-sm text-center">
<thead class="bg-gray-100 text-s font-semibold uppercase text-gray-700">
<tr>
<th rowspan="2" class="border px-4 py-2" style="width: 60px;">Patrol Round</th>
<th rowspan="2" class="border px-4 py-2">Guard Name</th>
<th colspan="3" class="border px-4 py-2">Patrol</th>
@php
$newSeqNoCnt = $seqNoCnt+1;
@endphp
<th colspan="{{ $newSeqNoCnt }}" class="border px-4 py-2">Sequences</th>
</tr>
<tr>
<th class="border px-4 py-2">Start Time</th>
<th class="border px-4 py-2">End Time</th>
<th class="border px-4 py-2">Lap Time</th>
{{-- <th class="border px-4 py-2" title="<?php echo htmlspecialchars($seqTitle1); ?>">1</th> --}}
@forelse($startSeqCheckPoints as $seq => $checkpoint)
@php
$seqTitle = '';
if ($checkpoint && $checkpoint->checkPointNames1 && $checkpoint->checkPointNames2) {
$seqTitle = $checkpoint->checkPointNames1->name . " - " . $checkpoint->checkPointNames2->name;
}
@endphp
@if ($seq == 1)
<th class="border px-4 py-2" style="" title="">1</th>
@endif
<th class="border px-4 py-2" title="{{ $seqTitle }}">{{ $seq + 1 }}</th>
@empty
<th class="border px-4 py-2" title="Sequences not found!">0</th>
@endforelse
</tr>
</thead>
<tbody class="divide-y divide-gray-100">
@forelse ($records as $index => $record)
<tr class="hover:bg-gray-50">
<td class="border px-4 py-2" title="Patrol Round">{{ $index + 1 }}</td>
{{-- <td class="text-center border px-4 py-2 whitespace-nowrap">{{ $record['created_at'] ?? '' }}</td> --}}
<td class="border px-4 py-2" title="Guard Name">{{ $record['guard_name'] ?? '' }}</td>
<td class="border px-4 py-2" title="Start Time">{{ $record['start_time'] ?? '' }}</td>
<td class="border px-4 py-2" title="End Time">{{ $record['end_time'] ?? '' }}</td>
<td class="border px-4 py-2" title="Lap Time">{{ $record['lap_time'] ?? '' }}</td>
<td class="border px-4 py-2" title="">0</td>
{{-- <td class="border px-4 py-2" style="{{ $bgStyle1 }}" title="<?php echo htmlspecialchars($seqTitle1); ?>">{{ $actualVal1 }}</td> --}}
@foreach($startSeqCheckPoints as $seq => $checkpoint)
@php
$seqTitle = '';
if ($checkpoint && $checkpoint->checkPointNames1 && $checkpoint->checkPointNames2) {
$seqTitle = $checkpoint->checkPointNames1->name . " - " . $checkpoint->checkPointNames2->name;
}
$actualVal = $record["Sequence_$seq"] ?? 'X';
$minVal = $checkpoint->min_cushioning;
$maxVal = $checkpoint->max_cushioning;// . "\n"
$seqTitTime = $seqTitle . "\n". $record["Sequence_Time_$seq"] ?? '';
if ($actualVal == 'X') {
$bgStyle = 'background-color: #f87171;';// 'bg-red-500'; // Tailwind red
} elseif (is_numeric($actualVal) && is_numeric($minVal) && is_numeric($maxVal)) {
if ($actualVal < $minVal) {
$bgStyle = 'background-color: #fde68a;';// 'bg-yellow-300'; // Tailwind yellow
} elseif ($actualVal > $maxVal) {
$bgStyle = 'background-color: #f87171;';// 'bg-red-500'; // Tailwind red
} else {
$bgStyle = 'background-color: #4ade80;';// 'bg-green-400'; // Tailwind green
}
} else {
$bgStyle = '';
}
@endphp
<td class="border px-4 py-2" style="{{ $bgStyle }}" title="{{ $seqTitTime }}">{{ $actualVal }}</td>
@endforeach
</tr>
@empty
<tr>
<td colspan="14" class="px-4 py-4 text-center text-gray-500">
No records found.
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>

View File

@@ -0,0 +1,219 @@
<div>
<div class="mb-4">
<h2 class="text-lg font-bold text-gray-800">
@if ($hasSearched)
SERIAL INVOICE DATA TABLE ( No of Packages to Scan : <strong>{{ $packageCount }}</strong> )
@elseif ($materialInvoice)
MATERIAL INVOICE DATA TABLE
@else
@if ($completedInvoice)
@if ($isSerial)
SERIAL INVOICE DATA TABLE ( No of Packages to Scan : <strong>{{ $packageCount }}</strong> )
@else
MATERIAL INVOICE DATA TABLE
@endif
@else
INVOICE DATA TABLE ( No of Packages to Scan : <strong>{{ $packageCount }}</strong> )
@endif
@endif
</h2>
<div class="mt-2">
<hr class="border-t-2 border-gray-300">
</div>
</div>
{{-- Modal for completed invoice--}}
@if ($completedInvoice)
<div class="text-center text-red-500">
<p>
@if ($isSerial)
Completed the scanning process for serial invoice number <strong>{{ $invoiceNumber }}</strong>.
@else
Completed the scanning process for material invoice number <strong>{{ $invoiceNumber }}</strong>.
@endif
</p>
</div>
@endif
{{-- Modal for empty invoice--}}
@if ($emptyInvoice)
<div class="text-center text-red-500">
<p>No data found for invoice number <strong>{{ $invoiceNumber }}</strong>.</p>
</div>
@endif
{{-- Modal for serial invoice--}}
@if ($hasSearched)
<div class="overflow-x-auto overflow-y-visible" style="height: 385px;">
{{-- <table class="min-w-[1500px] text-sm text-center border border-gray-300"> --}}
{{-- <table class="table-fixed min-w-[1500px] text-sm text-center border border-gray-300"> --}}
<table class="min-w-full text-sm text-center border border-gray-300">
<thead class="bg-gray-100 font-bold">
<tr>
<th class="border px-4 py-2">No</th>
<th class="border px-4 py-2">Material Code</th>
<th class="border px-4 py-2">Serial Number</th>
<th class="border px-4 py-2">Motor Scanned Status</th>
<th class="border px-4 py-2">Pump Scanned Status</th>
<th class="border px-4 py-2">Capacitor Scanned Status</th>
<th class="border px-4 py-2">Scanned Status Set</th>
<th class="border px-4 py-2">Scanned Status</th>
<th class="border px-4 py-2 w-[300px] whitespace-nowrap">Time Stamp</th>
<th class="border px-4 py-2">Operator ID</th>
<th class="border px-4 py-2">Panel Box Supplier</th>
<th class="border px-4 py-2">Panel Box Serial Number</th>
</tr>
</thead>
<tbody>
@forelse ($invoiceData as $index => $row)
<tr class="border-t">
<td class="border px-4 py-2">{{ $index + 1 }}</td>
<td class="border px-4 py-2">{{ $row['code'] ?? 'N/A' }}</td>
<td class="border px-4 py-2">{{ $row['serial_number'] ?? 'N/A' }}</td>
<td class="border px-4 py-2">{{ $row['motor_scanned_status'] ?? '' }}</td>
<td class="border px-4 py-2">{{ $row['pump_scanned_status'] ?? '' }}</td>
<td class="border px-4 py-2">{{ $row['capacitor_scanned_status'] ?? '' }}</td>
<td class="border px-4 py-2">{{ $row['scanned_status_set'] ?? '' }}</td>
<td class="border px-4 py-2">{{ $row['scanned_status'] ?? '' }}</td>
<td class="border px-4 py-2">{{ $row['created_at'] ?? '' }}</td>
<td class="border px-4 py-2">{{ $row['operator_id'] ?? '' }}</td>
<td class="border px-4 py-2">{{ $row['panel_box_supplier'] ?? '' }}</td>
<td class="border px-4 py-2">{{ $row['panel_box_serial_number'] ?? '' }}</td>
</tr>
@empty
<tr>
<td colspan="12" class="text-center py-4 text-gray-500">
No data found for invoice number <strong>{{ $invoiceNumber }}</strong>.
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
@endif
{{-- Modal for Capacitor Input --}}
<div>
<button wire:click="$set('showCapacitorInput', true)"></button>
@if($showCapacitorInput)
<div class="fixed inset-0 z-[9999] bg-black bg-opacity-50 flex items-center justify-center">
<div style="background:white; border:4px solid orange;" class="p-6 rounded-xl shadow-2xl w-[450px]">
<h3 class="text-xl font-semibold text-orange-700 mb-4">
Scan the Panel Box Supplier/Item Code/Serial Number
</h3>
<input
type="text"
id="capacitorInput"
autocomplete="off"
wire:model.defer="capacitorInput"
wire:keydown.enter.prevent="processCapacitorInput"
class="w-full border border-orange-300 rounded px-3 py-2 focus:outline-none focus:ring-0 focus:border-orange-300"
placeholder="Scan the panel box QR code"
{{-- autofocus --}}
onload="this.focus(); this.select();"
{{-- onfocus="this.select();" --}}
/>
<div class="flex justify-end gap-2 mt-4">
<button type="button" wire:click="cancelCapacitorInput"
class="mt-6 ml-10 bg-gray-300 hover:bg-gray-400 px-4 py-2 rounded transition">
Cancel
</button>
</div>
</div>
</div>
{{-- Add this script to focus on the input --}}
<script>
document.getElementById('capacitorInput').focus();
</script>
@endif
</div>
{{-- Modal for material invoice--}}
@if($materialInvoice)
<div class="overflow-x-auto overflow-y-visible" style="height: 385px;">
{{-- <table class="min-w-[1500px] text-sm text-center border border-gray-300"> --}}
{{-- <table class="table-fixed min-w-[1500px] text-sm text-center border border-gray-300"> --}}
<table class="min-w-full text-sm text-center border border-gray-300">
<thead class="bg-gray-100 font-bold">
<tr>
<th class="border px-4 py-2">No</th>
<th class="border px-4 py-2">Material Code</th>
<th class="border px-4 py-2">Material Type</th>
<th class="border px-4 py-2">Material Quantity</th>
<th class="border px-4 py-2">Serial Number</th>
<th class="border px-4 py-2">Batch Number</th>
<th class="border px-4 py-2">TimeStamp</th>
<th class="border px-4 py-2">Operator ID</th>
</tr>
</thead>
<tbody>
@forelse ($invoiceData as $index => $row)
<tr class="border-t">
<td class="border px-4 py-2">{{ $index + 1 }}</td>
<td class="border px-4 py-2">{{ $row['code'] ?? 'N/A' }}</td>
<td class="border px-4 py-2">{{ $row['material_type'] ?? 'N/A' }}</td>
<td class="border px-4 py-2">{{ $row['quantity'] ?? 'N/A' }}</td>
<td class="border px-4 py-2">{{ $row['serial_number'] ?? 'N/A' }}</td>
<td class="border px-4 py-2">{{ $row['batch_number'] ?? 'N/A' }}</td>
<td class="border px-4 py-2">{{ $row['created_at'] ?? 'N/A' }}</td>
<td class="border px-4 py-2">{{ $row['operator_id'] ?? 'N/A' }}</td>
</tr>
@empty
<tr>
<td colspan="10" class="text-center py-4 text-gray-500">
No data found for invoice number <strong>{{ $invoiceNumber }}</strong>.
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
@endif
</div>
{{-- <script>
// Clear input and set focus on form load
document.addEventListener('DOMContentLoaded', function () {
const input = document.getElementById('capacitorInput');
input.value = ''; // Clear the input field
input.focus(); // Set focus to the input field
});
</script> --}}
<script>
window.addEventListener('focus-capacitor-input', () => {
setTimeout(() => {
const input = document.getElementById('capacitorInput');
if (input) {
input.focus();
input.select();
}
}, 50);
});
window.addEventListener('focus-serial-number', () => {
setTimeout(() => {
const container = document.getElementById('serial_number_input');
const input = container?.querySelector('input'); // gets the actual input inside
if (input) {
input.focus();
input.select();
}
}, 50);
});
window.addEventListener('focus-invoice-number', () => {
setTimeout(() => {
const container = document.getElementById('invoice_number_input');
const input = container?.querySelector('input'); // gets the actual input inside
if (input) {
input.focus();
input.select();
}
}, 50);
});
</script>

View File

@@ -0,0 +1,55 @@
<div class="p-4">
<h2 class="text-lg font-bold mb-4 text-gray-700 uppercase tracking-wider">
INVOICE DATA STATUS TABLE:
</h2>
<div class="overflow-x-auto rounded-lg shadow">
<table class="w-full divide-y divide-gray-200 text-sm text-center">
<thead class="bg-gray-100 text-s font-semibold uppercase text-gray-700">
<tr>
<th class="border px-4 py-2">No</th>
<th class="border px-4 py-2">Created Datetime</th>
<th class="border px-4 py-2">Created By</th>
<th class="border px-4 py-2">Serial Number/Quantity</th>
<th class="border px-4 py-2">Pallet Number</th>
<th class="border px-4 py-2">Locator Number</th>
<th class="border px-4 py-2">Scanned Status</th>
<th class="border px-4 py-2">Scanned Datetime</th>
<th class="border px-4 py-2">Scanned By</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-100">
@forelse ($records as $index => $record)
<tr class="hover:bg-gray-50">
<td class="border px-4 py-2">{{ $index + 1 }}</td>
<td class="border px-4 py-2 whitespace-nowrap">{{ $record['created_at'] ?? '' }}</td>
<td class="border px-4 py-2">{{ $record['created_by'] ?? '' }}</td>
<td class="border px-4 py-2">{{ $record['serial_number'] ?? '' }}</td>
<td class="border px-4 py-2">{{ $record['pallet_number'] ?? '' }}</td>
<td class="border px-4 py-2 whitespace-nowrap">{{ $record['locator_number'] ?? '' }}</td>
<td class="border px-4 py-2">
@php
$status = $record['scanned_status'] ?? '';
@endphp
<span @class([
'text-green-600 font-semibold' => $status === 'Scanned',
'text-yellow-600 font-semibold' => $status === 'Incompleted',
'text-red-600 font-semibold' => $status === 'Not Exist',
])>
{{ $status }}
</span>
</td>
<td class="border px-4 py-2 whitespace-nowrap">{{ $record['scanned_at'] ?? '' }}</td>
<td class="border px-4 py-2">{{ $record['scanned_by'] ?? '' }}</td>
</tr>
@empty
<tr>
<td colspan="9" class="px-4 py-4 text-center text-gray-500">
No records found.
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>

View File

@@ -0,0 +1,86 @@
<div class="p-4">
@if ($reworkTyp == null || $reworkTyp == '')
<h2 class="text-lg text-center font-bold mb-4 uppercase tracking-wider" style="padding-top: 10px">
Choose 'Plant and Rework Type' then scan valid 'Invoice or Pallet' number to proceed..!
</h2>
@elseif ($reworkTyp == 'invoice')
{{-- No data available for the selected plant and production order! --}}
<h2 class="text-lg font-bold mb-4 text-gray-700 uppercase tracking-wider">
REWORK INVOICE DATA TABLE :
</h2>
<div class="overflow-x-auto rounded-lg shadow">
<table class="w-full divide-y divide-gray-200 text-sm text-center">
<thead class="bg-gray-100 text-s font-semibold uppercase text-gray-700">
<tr>
<th class="border px-4 py-2">No</th>
<th class="border px-4 py-2">Created Datetime</th>
<th class="border px-4 py-2">Created By</th>
<th class="border px-4 py-2">Serial Number</th>
<th class="border px-4 py-2">Pallet Number</th>
<th class="border px-4 py-2">Locator Number</th>
<th class="border px-4 py-2">Scanned Status</th>
<th class="border px-4 py-2">Scanned Datetime</th>
<th class="border px-4 py-2">Scanned By</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-100">
@forelse ($records as $index => $record)
<tr class="hover:bg-gray-50">
<td class="border px-4 py-2">{{ $index + 1 }}</td>
<td class="border px-4 py-2 whitespace-nowrap">{{ $record['created_at'] ?? '' }}</td>
<td class="border px-4 py-2">{{ $record['created_by'] ?? '' }}</td>
<td class="border px-4 py-2">{{ $record['serial_number'] ?? '' }}</td>
<td class="border px-4 py-2">{{ $record['pallet_number'] ?? '' }}</td>
<td class="border px-4 py-2 whitespace-nowrap">{{ $record['locator_number'] ?? '' }}</td>
<td class="border px-4 py-2">{{ $record['scanned_status'] ?? '' }}</td>
<td class="border px-4 py-2 whitespace-nowrap">{{ $record['scanned_at'] ?? '' }}</td>
<td class="border px-4 py-2">{{ $record['scanned_by'] ?? '' }}</td>
</tr>
@empty
<tr>
<td colspan="9" class="px-4 py-4 text-center text-gray-500">
No records found.
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
@elseif ($reworkTyp == 'pallet')
<h2 class="text-lg font-bold mb-4 text-gray-700 uppercase tracking-wider">
REWORK PALLET DATA TABLE :
</h2>
<div class="overflow-x-auto rounded-lg shadow">
<table class="w-full divide-y divide-gray-200 text-sm text-center">
<thead class="bg-gray-100 text-s font-semibold uppercase text-gray-700">
<tr>
<th class="border px-4 py-2">No</th>
<th class="border px-4 py-2">Created Datetime</th>
<th class="border px-4 py-2">Created By</th>
<th class="border px-4 py-2">Serial Number</th>
<th class="border px-4 py-2">Scanned Datetime</th>
<th class="border px-4 py-2">Scanned By</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-100">
@forelse ($records as $index => $record)
<tr class="hover:bg-gray-50">
<td class="border px-4 py-2">{{ $index + 1 }}</td>
<td class="border px-4 py-2 whitespace-nowrap">{{ $record['created_at'] ?? '' }}</td>
<td class="border px-4 py-2">{{ $record['created_by'] ?? '' }}</td>
<td class="border px-4 py-2">{{ $record['serial_number'] ?? '' }}</td>
<td class="border px-4 py-2 whitespace-nowrap">{{ $record['scanned_at'] ?? '' }}</td>
<td class="border px-4 py-2">{{ $record['scanned_by'] ?? '' }}</td>
</tr>
@empty
<tr>
<td colspan="9" class="px-4 py-4 text-center text-gray-500">
No records found.
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
@endif
</div>

View File

@@ -0,0 +1,44 @@
<div class="p-4">
<h2 class="text-lg font-bold mb-4 text-gray-700 uppercase tracking-wider">
LOCATOR DATA TABLE:
</h2>
<div class="overflow-x-auto rounded-lg shadow">
<table class="w-full divide-y divide-gray-200 text-sm text-center">
<thead class="bg-gray-100 text-s font-semibold uppercase text-gray-700">
<tr>
<th class="border px-4 py-2">No</th>
<th class="border px-4 py-2">Created Datetime</th>
<th class="border px-4 py-2">Created By</th>
<th class="border px-4 py-2">Pallet Number</th>
<th class="border px-4 py-2">Serial Number</th>
<th class="border px-4 py-2">Locator Number</th>
<th class="border px-4 py-2">Locator Quantity</th>
<th class="border px-4 py-2">Updated Datetime</th>
<th class="border px-4 py-2">Updated By</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-100">
@forelse ($locators as $index => $locator)
<tr class="hover:bg-gray-50">
<td class="border px-4 py-2">{{ $index + 1 }}</td>
<td class="border px-4 py-2 whitespace-nowrap">{{ $locator['created_at']?->format('Y-m-d H:i:s') ?? '-' }}</td>
<td class="border px-4 py-2">{{ $locator['created_by'] ?? '-' }}</td>
<td class="border px-4 py-2 whitespace-nowrap">{{ $locator['pallet_number'] ?? '-' }}</td>
<td class="border px-4 py-2">{{ $locator['serial_number'] ?? '-' }}</td>
<td class="border px-4 py-2 whitespace-nowrap">{{ $locator['locator_number'] ?? '-' }}</td>
<td class="border px-4 py-2">{{ $locator['locator_quantity'] ?? '-' }}</td>
<td class="border px-4 py-2 whitespace-nowrap">{{ $locator['updated_at']?->format('Y-m-d H:i:s') ?? '-' }}</td>
<td class="border px-4 py-2">{{ $locator['updated_by'] ?? '-' }}</td>
</tr>
@empty
<tr>
<td colspan="9" class="px-4 py-4 text-center text-gray-500">
No locator records found.
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>

View File

@@ -0,0 +1,45 @@
<div class="p-4">
<h2 class="text-lg font-bold mb-4 text-gray-700 uppercase tracking-wider">
INVOICE DATA TABLE:
</h2>
<div class="overflow-x-auto rounded-lg shadow">
<table class="w-full divide-y divide-gray-200 text-sm text-center">
<thead class="bg-gray-100 text-s font-semibold uppercase text-gray-700">
<tr>
<th class="border px-4 py-2">No</th>
<th class="border px-4 py-2">Created Datetime</th>
<th class="border px-4 py-2">Created By</th>
<th class="border px-4 py-2">Serial Number</th>
<th class="border px-4 py-2">Pallet Number</th>
<th class="border px-4 py-2">Locator Number</th>
<th class="border px-4 py-2">Scanned Status</th>
<th class="border px-4 py-2">Scanned Datetime</th>
<th class="border px-4 py-2">Scanned By</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-100">
@forelse ($records as $index => $record)
<tr class="hover:bg-gray-50">
<td class="border px-4 py-2">{{ $index + 1 }}</td>
<td class="border px-4 py-2 whitespace-nowrap">{{ $record['created_at'] ?? '' }}</td>
<td class="border px-4 py-2">{{ $record['created_by'] ?? '' }}</td>
<td class="border px-4 py-2">{{ $record['serial_number'] ?? '' }}</td>
<td class="border px-4 py-2">{{ $record['pallet_number'] ?? '' }}</td>
<td class="border px-4 py-2">{{ $record['locator_number'] ?? '' }}</td>
<td class="border px-4 py-2">{{ $record['scanned_status'] ?? '' }}</td>
<td class="border px-4 py-2 whitespace-nowrap">{{ $record['scanned_at'] ?? '' }}</td>
<td class="border px-4 py-2">{{ $record['scanned_by'] ?? '' }}</td>
</tr>
@empty
<tr>
<td colspan="9" class="px-4 py-4 text-center text-gray-500">
No records found.
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>

View File

@@ -0,0 +1,31 @@
{{-- <div>
{{-- Do your work, then step back.
</div> --}}
<div>
@push('scripts')
<script>
window.addEventListener('play-notification-sound', () => {
const audio = new Audio('/sounds/click.mp3');
audio.play().catch(() => {
console.log("Sound blocked until user interacts with the page");
});
});
window.addEventListener('play-warn-sound', () => {
const audio = new Audio('/sounds/warning.mp3');
audio.play().catch(() => {
console.log("Sound blocked until user interacts with the page");
});
});
window.addEventListener('play-beep-sound', () => {
const audio = new Audio('/sounds/scanner-beep.mp3');
audio.play().catch(() => {
console.log("Sound blocked until user interacts with the page");
});
});
</script>
@endpush
</div>

View File

@@ -0,0 +1,40 @@
<div class="p-4">
<h2 class="text-lg font-bold mb-4 text-gray-700 uppercase tracking-wider">
PALLET DATA TABLE:
</h2>
<div class="overflow-x-auto rounded-lg shadow">
<table class="w-full divide-y divide-gray-200 text-sm text-center">
<thead class="bg-gray-100 text-s font-semibold uppercase text-gray-700">
<tr>
<th class="border px-4 py-2">No</th>
<th class="border px-4 py-2">Created Datetime</th>
<th class="border px-4 py-2">Created By</th>
<th class="border px-4 py-2">Pallet Number</th>
<th class="border px-4 py-2">Serial Number</th>
<th class="border px-4 py-2">Scanned Datetime</th>
<th class="border px-4 py-2">Scanned By</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-100">
@forelse ($records as $index => $record)
<tr class="hover:bg-gray-50">
<td class="border px-4 py-2">{{ $index + 1 }}</td>
<td class="border px-4 py-2 whitespace-nowrap">{{ $record['created_at'] ?? '-' }}</td>
<td class="border px-4 py-2">{{ $record['created_by'] ?? '-' }}</td>
<td class="border px-4 py-2 whitespace-nowrap">{{ $record['pallet_number'] ?? '-' }}</td>
<td class="border px-4 py-2">{{ $record['serial_number'] ?? '-' }}</td>
<td class="border px-4 py-2 whitespace-nowrap">{{ $record['scanned_at'] ?? '-' }}</td>
<td class="border px-4 py-2">{{ $record['scanned_by'] ?? '-' }}</td>
</tr>
@empty
<tr>
<td colspan="7" class="px-4 py-4 text-center text-gray-500">
No pallet records found.
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>

View File

@@ -0,0 +1,42 @@
<div class="p-4">
<h2 class="text-lg font-bold mb-4 text-gray-700 uppercase tracking-wider">
PALLET DATA TABLE:
</h2>
<div class="overflow-x-auto rounded-lg shadow">
<table class="w-full divide-y divide-gray-200 text-sm text-center">
<thead class="bg-gray-100 text-s font-semibold uppercase text-gray-700">
<tr>
<th class="border px-4 py-2">No</th>
<th class="border px-4 py-2">Created Datetime</th>
<th class="border px-4 py-2">Created By</th>
<th class="border px-4 py-2">Pallet Number</th>
<th class="border px-4 py-2">Serial Number</th>
<th class="border px-4 py-2">Scanned Datetime</th>
<th class="border px-4 py-2">Scanned By</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-100">
@forelse ($records as $index => $record)
<tr class="hover:bg-gray-50">
<td class="border px-4 py-2">{{ $index + 1 }}</td>
<td class="border px-4 py-2">{{ $record['created_at'] ?? '' }}</td>
<td class="border px-4 py-2">{{ $record['created_by'] ?? '' }}</td>
<td class="border px-4 py-2">{{ $record['pallet_number'] ?? '' }}</td>
<td class="border px-4 py-2">{{ $record['serial_number'] ?? '' }}</td>
<td class="border px-4 py-2">{{ $record['scanned_at'] ?? '' }}</td>
<td class="border px-4 py-2">{{ $record['scanned_by'] ?? '' }}</td>
</tr>
@empty
<tr>
<td colspan="7" class="px-4 py-4 text-center text-gray-500">
No pallet records found.
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>

View File

@@ -0,0 +1,40 @@
<div class="w-full px-2 py-2">
<table class="w-full divide-y divide-gray-200 border-1 rounded-lg overflow-hidden">
<thead class="bg-gray-100">
<tr>
<th class="px-2 py-2 text-center border text-xs font-bold text-gray-700 uppercase tracking-wider">No</th>
<th class="px-2 py-2 text-center border text-xs font-bold text-gray-700 uppercase tracking-wider">Item Code</th>
<th class="px-2 py-2 text-center border text-xs font-bold text-gray-700 uppercase tracking-wider">Serial Number</th>
<th class="px-2 py-2 text-center border text-xs font-bold text-gray-700 uppercase tracking-wider">Created At</th>
<th class="px-2 py-2 text-center border text-xs font-bold text-gray-700 uppercase tracking-wider">SAP Status</th>
<th class="px-2 py-2 text-center border text-xs font-bold text-gray-700 uppercase tracking-wider">SAP Description</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
@forelse($items as $index => $item)
<tr>
<td class="px-3 py-2 border text-center text-xs whitespace-nowrap">{{ $index + 1 }}</td>
<td class="px-3 py-2 border text-center text-xs whitespace-nowrap">{{ $item['item_code'] }}</td>
<td class="px-3 py-2 border text-center text-xs whitespace-nowrap">{{ $item['serial_number'] }}</td>
<td class="px-3 py-2 border text-center text-xs whitespace-nowrap">{{ $item['created_at'] }}</td>
<td class="px-3 py-2 border text-center text-xs whitespace-nowrap">{{ $item['sap_status'] }}</td>
<td class="px-3 py-2 border text-center text-xs whitespace-nowrap">{{ $item['sap_description'] }}</td>
</tr>
@empty
@if ($attempted)
<tr>
<td colspan="6" class="px-3 py-4 border text-center text-sm text-gray-500">
No data available for the selected plant and production order!
</td>
</tr>
@elseif ($refresh)
<tr>
<td colspan="6" class="px-3 py-4 border text-center text-sm text-gray-500">
Scan the valid production order and press enter!
</td>
</tr>
@endif
@endforelse
</tbody>
</table>
</div>

View File

@@ -0,0 +1,40 @@
<div class="w-full px-2 py-2">
<table class="w-full divide-y divide-gray-200 border-1 rounded-lg overflow-hidden">
<thead class="bg-gray-100">
<tr>
<th class="px-2 py-2 text-center border text-xs font-bold text-gray-700 uppercase tracking-wider">No</th>
<th class="px-2 py-2 text-center border text-xs font-bold text-gray-700 uppercase tracking-wider">Item Code</th>
<th class="px-2 py-2 text-center border text-xs font-bold text-gray-700 uppercase tracking-wider">Serial Number</th>
<th class="px-2 py-2 text-center border text-xs font-bold text-gray-700 uppercase tracking-wider">Created At</th>
<th class="px-2 py-2 text-center border text-xs font-bold text-gray-700 uppercase tracking-wider">SAP Status</th>
<th class="px-2 py-2 text-center border text-xs font-bold text-gray-700 uppercase tracking-wider">SAP Description</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
@forelse($items as $index => $item)
<tr>
<td class="px-3 py-2 border text-center text-xs whitespace-nowrap">{{ $index + 1 }}</td>
<td class="px-3 py-2 border text-center text-xs whitespace-nowrap">{{ $item['item_code'] }}</td>
<td class="px-3 py-2 border text-center text-xs whitespace-nowrap">{{ $item['serial_number'] }}</td>
<td class="px-3 py-2 border text-center text-xs whitespace-nowrap">{{ $item['created_at'] }}</td>
<td class="px-3 py-2 border text-center text-xs whitespace-nowrap">{{ $item['sap_status'] }}</td>
<td class="px-3 py-2 border text-center text-xs whitespace-nowrap">{{ $item['sap_description'] }}</td>
</tr>
@empty
@if ($attempted)
<tr>
<td colspan="6" class="px-3 py-4 border text-center text-sm text-gray-500">
No data available for the selected plant and production order!
</td>
</tr>
@elseif ($refresh)
<tr>
<td colspan="6" class="px-3 py-4 border text-center text-sm text-gray-500">
Scan the valid production order and press enter!
</td>
</tr>
@endif
@endforelse
</tbody>
</table>
</div>

View File

@@ -0,0 +1,118 @@
<div class="flex-none md:flex space-x-4 w-full">
<!-- First Table (08:00 AM to 19:00 PM) -->
<div class="w-full px-2 py-2">
<table class="w-full divide-y divide-gray-200 border-1 rounded-lg overflow-hidden">
<thead class="bg-gray-100">
<tr>
<th class="px-2 py-2 text-center border text-xs font-bold text-gray-700 uppercase tracking-wider">No</th>
<th class="px-2 py-2 text-center border text-xs font-bold text-gray-700 uppercase tracking-wider">Time Range</th>
<th class="px-2 py-2 text-center border text-xs font-bold text-gray-700 uppercase tracking-wider">Production Quantity</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
@php
// Static time range for the first 12 hours (08:00 AM to 07:00 PM)
$timeRanges = [
'08:00 AM - 09:00 AM',
'09:00 AM - 10:00 AM',
'10:00 AM - 11:00 AM',
'11:00 AM - 12:00 PM',
'12:00 PM - 13:00 PM',
'13:00 PM - 14:00 PM',
'14:00 PM - 15:00 PM',
'15:00 PM - 16:00 PM',
'16:00 PM - 17:00 PM',
'17:00 PM - 18:00 PM',
'18:00 PM - 19:00 PM',
'19:00 PM - 20:00 PM',
];
@endphp
@foreach ($timeRanges as $index => $timeRange)
<tr>
<td class="px-3 py-2 border text-center text-xs whitespace-nowrap">{{ $index + 1 }}</td>
<td class="px-3 py-2 border text-center text-xs whitespace-nowrap">{{ $timeRange }}</td>
<td class="px-3 py-2 border text-xs text-center whitespace-nowrap">
{{ $hourlyData[$index] ?? 0 }}
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
<div class="w-full px-2 py-2">
<table class="w-full divide-y divide-gray-200 border-1 rounded-lg overflow-hidden">
<thead class="bg-gray-100">
<tr>
<th class="px-2 py-2 text-center border text-xs font-bold text-gray-700 uppercase tracking-wider">No</th>
<th class="px-2 py-2 text-center border text-xs font-bold text-gray-700 uppercase tracking-wider">Time Range</th>
<th class="px-2 py-2 text-center border text-xs font-bold text-gray-700 uppercase tracking-wider">Production Quantity</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
@php
// Static time range for the first 12 hours (08:00 AM to 07:00 PM)
$timeRanges = [
'20:00 PM - 21:00 PM',
'21:00 PM - 22:00 PM',
'22:00 PM - 23:00 PM',
'23:00 PM - 12:00 AM',
'12:00 AM - 01:00 AM',
'01:00 AM - 02:00 AM',
'02:00 AM - 03:00 AM',
'03:00 AM - 04:00 AM',
'04:00 AM - 05:00 AM',
'05:00 AM - 06:00 AM',
'06:00 AM - 07:00 AM',
'07:00 AM - 08:00 AM',
];
@endphp
@foreach ($timeRanges as $index => $timeRange)
<tr>
<td class="px-3 py-2 border text-center text-xs whitespace-nowrap">{{ $index + 13 }}</td>
<td class="px-3 py-2 border text-center text-xs whitespace-nowrap">{{ $timeRange }}</td>
<td class="px-3 py-2 border text-xs text-center whitespace-nowrap">
{{ $hourlyData[$index + 12] ?? 0 }}
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
<!-- Second Table (20:00 PM to 08:00 AM next day) -->
{{-- <div class="w-full px-2">
<table class="w-full divide-y divide-gray-200 border-1 rounded-lg overflow-hidden">
<thead class="bg-gray-100">
<tr>
<th class="px-3 py-2 border text-center text-xs font-bold text-gray-800 uppercase tracking-wider">No</th>
<th class="px-3 py-2 border text-center text-xs font-bold text-gray-800 uppercase tracking-wider">Time Range</th>
<th class="px-3 py-2 border text-center text-xs font-bold text-gray-800 uppercase tracking-wider">Production Quantity</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
<tr><td class="px-3 py-2 border text-center text-xs whitespace-nowrap">13</td><td class="px-3 py-2 border text-center text-xs whitespace-nowrap">08:00 PM - 09:00 PM</td><td class="px-3 py-2 border text-xs text-center whitespace-nowrap">{{ $productionQuantities[12] }}</td></tr>
<tr><td class="px-3 py-2 border text-center text-xs whitespace-nowrap">14</td><td class="px-3 py-2 border text-center text-xs whitespace-nowrap">09:00 PM - 10:00 PM</td><td class="px-3 py-2 border text-xs text-center whitespace-nowrap">{{ $productionQuantities[13] }}</td></tr>
<tr><td class="px-3 py-2 border text-center text-xs whitespace-nowrap">15</td><td class="px-3 py-2 border text-center text-xs whitespace-nowrap">10:00 PM - 11:00 PM</td><td class="px-3 py-2 border text-xs text-center whitespace-nowrap">{{ $productionQuantities[14] }}</td></tr>
<tr><td class="px-3 py-2 border text-center text-xs whitespace-nowrap">16</td><td class="px-3 py-2 border text-center text-xs whitespace-nowrap">11:00 PM - 12:00 AM</td><td class="px-3 py-2 border text-xs text-center whitespace-nowrap">{{ $productionQuantities[15] }}</td></tr>
<tr><td class="px-3 py-2 border text-center text-xs whitespace-nowrap">17</td><td class="px-3 py-2 border text-center text-xs whitespace-nowrap">12:00 AM - 01:00 AM</td><td class="px-3 py-2 border text-xs text-center whitespace-nowrap">{{ $productionQuantities[16] }}</td></tr>
<tr><td class="px-3 py-2 border text-center text-xs whitespace-nowrap">18</td><td class="px-3 py-2 border text-center text-xs whitespace-nowrap">01:00 AM - 02:00 AM</td><td class="px-3 py-2 border text-xs text-center whitespace-nowrap">{{ $productionQuantities[17] }}</td></tr>
<tr><td class="px-3 py-2 border text-center text-xs whitespace-nowrap">19</td><td class="px-3 py-2 border text-center text-xs whitespace-nowrap">02:00 AM - 03:00 AM</td><td class="px-3 py-2 border text-xs text-center whitespace-nowrap">{{ $productionQuantities[18] }}</td></tr>
<tr><td class="px-3 py-2 border text-center text-xs whitespace-nowrap">20</td><td class="px-3 py-2 border text-center text-xs whitespace-nowrap">03:00 AM - 04:00 AM</td><td class="px-3 py-2 border text-xs text-center whitespace-nowrap">{{ $productionQuantities[19] }}</td></tr>
<tr><td class="px-3 py-2 border text-center text-xs whitespace-nowrap">21</td><td class="px-3 py-2 border text-center text-xs whitespace-nowrap">04:00 AM - 05:00 AM</td><td class="px-3 py-2 border text-xs text-center whitespace-nowrap">{{ $productionQuantities[20] }}</td></tr>
<tr><td class="px-3 py-2 border text-center text-xs whitespace-nowrap">22</td><td class="px-3 py-2 border text-center text-xs whitespace-nowrap">05:00 AM - 06:00 AM</td><td class="px-3 py-2 border text-xs text-center whitespace-nowrap">{{ $productionQuantities[21] }}</td></tr>
<tr><td class="px-3 py-2 border text-center text-xs whitespace-nowrap">23</td><td class="px-3 py-2 border text-center text-xs whitespace-nowrap">06:00 AM - 07:00 AM</td><td class="px-3 py-2 border text-xs text-center whitespace-nowrap">{{ $productionQuantities[22] }}</td></tr>
<tr><td class="px-3 py-2 border text-center text-xs whitespace-nowrap">24</td><td class="px-3 py-2 border text-center text-xs whitespace-nowrap">07:00 AM - 08:00 AM</td><td class="px-3 py-2 border text-xs text-center whitespace-nowrap">{{ $productionQuantities[23] }}</td></tr>
</tbody>
</table>
</div> --}}
</div>

View File

@@ -0,0 +1,58 @@
<div class="p-4">
<h2 class="text-lg font-bold mb-4 text-gray-700 uppercase tracking-wider">
SERIAL LOCATOR DATA TABLE:
</h2>
<div class="overflow-x-auto rounded-lg shadow">
<table class="w-full divide-y divide-gray-200 text-sm text-center">
<thead class="bg-gray-100 text-s font-semibold uppercase text-gray-700">
<tr>
<th class="border px-4 py-2">No</th>
<th class="border px-4 py-2">Created Datetime</th>
<th class="border px-4 py-2">Created By</th>
<th class="border px-4 py-2">Pallet Number</th>
<th class="border px-4 py-2">Serial Number</th>
<th class="border px-4 py-2">Locator Number</th>
<th class="border px-4 py-2">Locator Quantity</th>
<th class="border px-4 py-2">Updated Datetime</th>
<th class="border px-4 py-2">Updated By</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-100">
@forelse ($locators as $index => $locator)
<tr class="hover:bg-gray-50">
<td class="border px-4 py-2">{{ $index + 1 }}</td>
<td class="border px-4 py-2 whitespace-nowrap">{{ $locator->created_at?->format('Y-m-d H:i:s') ?? '' }}</td>
<td class="border px-4 py-2">{{ $locator->created_by ?? '' }}</td>
<td class="border px-4 py-2">{{ $locator->pallet_number ?? '' }}</td>
<td class="border px-4 py-2">{{ $locator->serial_number ?? '' }}</td>
<td class="border px-4 py-2">{{ $locator->locator_number ?? '' }}</td>
<td class="border px-4 py-2">{{ $locator->locator_quantity ?? '' }}</td>
<td class="border px-4 py-2 whitespace-nowrap">{{ $locator->updated_at?->format('Y-m-d H:i:s') ?? '' }}</td>
<td class="border px-4 py-2">{{ $locator->updated_by ?? '' }}</td>
</tr>
@empty
<tr>
@if ($hasSearched)
<td colspan="9" class="px-4 py-4 text-center text-gray-900 font-semibold">
@if ($locatorNumber && $serialNumber)
Serial Number "{{ $serialNumber }}" and Locator Number "{{ $locatorNumber }}" not found.
@elseif ($locatorNumber)
Locator Number "{{ $locatorNumber }}" not found.
@elseif ($serialNumber)
Serial Number "{{ $serialNumber }}" not found.
@else
No records found.
@endif
</td>
@else
<td colspan="9" class="px-4 py-4 text-center text-gray-900 font-semibold">
No records found.
</td>
@endif
</tr>
@endforelse
</tbody>
</table>
</div>
</div>

View File

@@ -0,0 +1,209 @@
<div>
<div class="mb-4">
<h2 class="text-lg font-bold text-gray-800">
@if ($hasSearched)
SERIAL INVOICE DATA TABLE
@elseif ($materialInvoice)
MATERIAL INVOICE DATA TABLE
@else
@if ($completedInvoice)
@if ($isSerial)
SERIAL INVOICE DATA TABLE
@else
MATERIAL INVOICE DATA TABLE
@endif
@else
INVOICE DATA TABLE
@endif
@endif
</h2>
<div class="mt-2">
<hr class="border-t-2 border-gray-300">
</div>
</div>
{{-- Modal for completed invoice--}}
@if ($completedInvoice)
<div class="text-center text-red-500">
<p>
@if ($isSerial)
Completed the scanning process for serial invoice number <strong>{{ $invoiceNumber }}</strong>.
@else
Completed the scanning process for material invoice number <strong>{{ $invoiceNumber }}</strong>.
@endif
</p>
</div>
@endif
{{-- Modal for empty invoice--}}
@if ($emptyInvoice)
<div class="text-center text-red-500">
<p>No data found for invoice number <strong>{{ $invoiceNumber }}</strong>.</p>
</div>
@endif
{{-- Modal for serial invoice--}}
@if ($hasSearched)
<div class="overflow-x-auto overflow-y-visible" style="height: 385px;">
{{-- <table class="min-w-[1500px] text-sm text-center border border-gray-300"> --}}
{{-- <table class="table-fixed min-w-[1500px] text-sm text-center border border-gray-300"> --}}
<table class="min-w-full text-sm text-center border border-gray-300">
<thead class="bg-gray-100 font-bold">
<tr>
<th class="border px-4 py-2">No</th>
<th class="border px-4 py-2">Material Code</th>
<th class="border px-4 py-2">Serial Number</th>
<th class="border px-4 py-2">Motor Scanned Status</th>
<th class="border px-4 py-2">Pump Scanned Status</th>
<th class="border px-4 py-2">Capacitor Scanned Status</th>
<th class="border px-4 py-2">Scanned Status Set</th>
<th class="border px-4 py-2">Scanned Status</th>
<th class="border px-4 py-2 w-[300px] whitespace-nowrap">Time Stamp</th>
<th class="border px-4 py-2">Operator ID</th>
<th class="border px-4 py-2">Panel Box Supplier</th>
<th class="border px-4 py-2">Panel Box Serial Number</th>
</tr>
</thead>
<tbody>
@forelse ($invoiceData as $index => $row)
<tr class="border-t">
<td class="border px-4 py-2">{{ $index + 1 }}</td>
<td class="border px-4 py-2">{{ $row['code'] ?? 'N/A' }}</td>
<td class="border px-4 py-2">{{ $row['serial_number'] ?? 'N/A' }}</td>
<td class="border px-4 py-2">{{ $row['motor_scanned_status'] ?? '' }}</td>
<td class="border px-4 py-2">{{ $row['pump_scanned_status'] ?? '' }}</td>
<td class="border px-4 py-2">{{ $row['capacitor_scanned_status'] ?? '' }}</td>
<td class="border px-4 py-2">{{ $row['scanned_status_set'] ?? '' }}</td>
<td class="border px-4 py-2">{{ $row['scanned_status'] ?? '' }}</td>
<td class="border px-4 py-2">{{ $row['created_at'] ?? '' }}</td>
<td class="border px-4 py-2">{{ $row['operator_id'] ?? '' }}</td>
<td class="border px-4 py-2">{{ $row['panel_box_supplier'] ?? '' }}</td>
<td class="border px-4 py-2">{{ $row['panel_box_serial_number'] ?? '' }}</td>
</tr>
@empty
<tr>
<td colspan="12" class="text-center py-4 text-gray-500">
No data found for invoice number <strong>{{ $invoiceNumber }}</strong>.
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
@endif
{{-- Modal for Capacitor Input --}}
<div>
<button wire:click="$set('showCapacitorInput', true)"></button>
@if($showCapacitorInput)
<div class="fixed inset-0 z-[9999] bg-black bg-opacity-50 flex items-center justify-center">
<div style="background:white; border:4px solid orange;" class="p-6 rounded-xl shadow-2xl w-[450px]">
<h3 class="text-xl font-semibold text-orange-700 mb-4">
Scan the Panel Box Supplier/Item Code/Serial Number
</h3>
<input
type="text"
id="capacitorInput"
autocomplete="off"
wire:model.defer="capacitorInput"
wire:keydown.enter.prevent="processCapacitorInput"
class="w-full border border-orange-300 rounded px-3 py-2 focus:outline-none focus:ring-0 focus:border-orange-300"
placeholder="Scan the panel box QR code"
{{-- autofocus --}}
onload="this.focus(); this.select();"
{{-- onfocus="this.select();" --}}
/>
<div class="flex justify-end gap-2 mt-4">
<button type="button" wire:click="cancelCapacitorInput"
class="mt-6 ml-10 bg-gray-300 hover:bg-gray-400 px-4 py-2 rounded transition">
Cancel
</button>
</div>
</div>
</div>
{{-- Add this script to focus on the input --}}
<script>
document.getElementById('capacitorInput').focus();
</script>
@endif
</div>
{{-- Modal for material invoice--}}
@if($materialInvoice)
<div class="overflow-x-auto overflow-y-visible" style="height: 385px;">
{{-- <table class="min-w-[1500px] text-sm text-center border border-gray-300"> --}}
{{-- <table class="table-fixed min-w-[1500px] text-sm text-center border border-gray-300"> --}}
<table class="min-w-full text-sm text-center border border-gray-300">
<thead class="bg-gray-100 font-bold">
<tr>
<th class="border px-4 py-2">No</th>
<th class="border px-4 py-2">Material Code</th>
<th class="border px-4 py-2">Material Type</th>
<th class="border px-4 py-2">Material Quantity</th>
<th class="border px-4 py-2">Serial Number</th>
<th class="border px-4 py-2">Batch Number</th>
<th class="border px-4 py-2">TimeStamp</th>
<th class="border px-4 py-2">Operator ID</th>
</tr>
</thead>
<tbody>
@forelse ($invoiceData as $index => $row)
<tr class="border-t">
<td class="border px-4 py-2">{{ $index + 1 }}</td>
<td class="border px-4 py-2">{{ $row['code'] ?? 'N/A' }}</td>
<td class="border px-4 py-2">{{ $row['material_type'] ?? 'N/A' }}</td>
<td class="border px-4 py-2">{{ $row['quantity'] ?? 'N/A' }}</td>
<td class="border px-4 py-2">{{ $row['serial_number'] ?? 'N/A' }}</td>
<td class="border px-4 py-2">{{ $row['batch_number'] ?? 'N/A' }}</td>
<td class="border px-4 py-2">{{ $row['created_at'] ?? 'N/A' }}</td>
<td class="border px-4 py-2">{{ $row['operator_id'] ?? 'N/A' }}</td>
</tr>
@empty
<tr>
<td colspan="10" class="text-center py-4 text-gray-500">
No data found for invoice number <strong>{{ $invoiceNumber }}</strong>.
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
@endif
</div>
{{-- <script>
// Clear input and set focus on form load
document.addEventListener('DOMContentLoaded', function () {
const input = document.getElementById('capacitorInput');
input.value = ''; // Clear the input field
input.focus(); // Set focus to the input field
});
</script> --}}
<script>
window.addEventListener('focus-capacitor-input', () => {
setTimeout(() => {
const input = document.getElementById('capacitorInput');
if (input) {
input.focus();
input.select();
}
}, 50);
});
window.addEventListener('focus-serial-number', () => {
setTimeout(() => {
const container = document.getElementById('serial_number_input');
const input = container?.querySelector('input'); // gets the actual input inside
if (input) {
input.focus();
input.select();
}
}, 100);
});
</script>

View File

@@ -0,0 +1,81 @@
{{-- <div class="overflow-x-auto overflow-y-visible" style="height: 385px;">
<table class="table-auto w-full border-collapse border">
<thead class="bg-gray-100">
<tr>
<th class="border p-2">No</th>
<th class="border p-2">Reference No</th>
<th class="border p-2">Serial Number</th>
<th class="border p-2">Created By</th>
</tr>
</thead>
<tbody>
@forelse($records as $index => $record)
<tr>
<td class="border p-2 text-center">{{ $index + 1 }}</td>
<td class="border p-2 text-center">{{ $refNumber }}</td>
<td class="border p-2 text-center">{{ $record['serial_number'] }}</td>
<td class="border p-2 text-center">{{ $record->created_by }}</td>
</tr>
@empty
<tr>
<td class="border p-2 text-center" colspan="4">No serial numbers found.</td>
</tr>
@endforelse
</tbody>
</table>
</div> --}}
<div>
<h3 class="text-lg font-semibold mb-2">Sticker Printing Table</h3>
<div
wire:loading.remove
@if(!$materialInvoice) style="display:none" @endif
class="overflow-x-auto overflow-y-visible"
style="height: 385px;"
>
<table class="table-auto w-full border-collapse border">
{{-- <thead class="bg-gray-100"> --}}
<thead class="bg-gray-100 text-xs">
<tr>
<th class="border p-2">No</th>
<th class="border p-2">Reference No</th>
<th class="border p-2">Serial Number</th>
<th class="border p-2">Created By</th>
</tr>
</thead>
{{-- <tbody> --}}
<tbody class="text-xs">
@forelse($records as $index => $record)
<tr>
<td class="border p-2 text-center">{{ $index + 1 }}</td>
<td class="border p-2 text-center">{{ $refNumber }}</td>
<td class="border p-2 text-center">{{ $record['serial_number'] }}</td>
<td class="border p-2 text-center">{{ $record->created_by }}</td>
</tr>
@empty
<tr>
<td class="border p-2 text-center" colspan="4">No serial numbers found.</td>
</tr>
@endforelse
</tbody>
</table>
</div>
<script>
window.addEventListener('focus-serial-number', () => {
setTimeout(() => {
const container = document.getElementById('serial_number_input');
const input = container?.querySelector('input'); // gets the actual input inside
if (input) {
input.focus();
input.select();
}
}, 50);
});
</script>

View File

@@ -0,0 +1,55 @@
<!DOCTYPE html>
<html>
<head>
<title>Calibration Table</title>
<style>
table {
border-collapse: collapse;
width: 60%;
}
th, td {
border: 1px solid #444;
padding: 8px;
text-align: left;
}
th {
background: #f2f2f2;
}
</style>
</head>
<body>
{{-- <h5>{{ $name }}</h5> --}}
{{-- <h5>{!! $name !!}</h5> --}}
<div style="text-align: center; font-weight: bold;">
{{ $company }}
</div>
<br>
<p>{!! $greeting !!}</p>
<table style="table-layout: auto; width: 100%; border-collapse: collapse;">
<thead>
<tr>
<th style="text-align: center; border: 1px solid #444; padding: 8px; background: #f2f2f2;">No</th>
<th style="text-align: center; border: 1px solid #444; padding: 8px; background: #f2f2f2; white-space: nowrap;">Plant</th>
<th style="text-align: center; border: 1px solid #444; padding: 8px; background: #f2f2f2; white-space: nowrap;">Line</th>
<th style="text-align: center; border: 1px solid #444; padding: 8px; background: #f2f2f2;">Target Quantity</th>
<th style="text-align: center; border: 1px solid #444; padding: 8px; background: #f2f2f2;">Line Type</th>
<th style="text-align: center; border: 1px solid #444; padding: 8px; background: #f2f2f2;">Production Quantity</th>
</tr>
</thead>
<tbody>
@foreach($tableData as $row)
<tr>
<td style="border: 1px solid #444; padding: 8px; text-align: center;">{{ $row['no'] }}</td>
<td style="border: 1px solid #444; padding: 8px; text-align: center; white-space: nowrap;">{{ $row['plant'] }}</td>
<td style="border: 1px solid #444; padding: 8px; text-align: center; white-space: nowrap;">{{ $row['line'] }}</td>
<td style="border: 1px solid #444; padding: 8px; text-align: center;">{{ $row['targetQuantity'] }}</td>
<td style="border: 1px solid #444; padding: 8px; text-align: center;">{{ $row['type'] }}</td>
<td style="border: 1px solid #444; padding: 8px; text-align: center;">{{ $row['productionQuantity'] }}</td>
</tr>
@endforeach
</tbody>
</table>
<p>{!! $wishes !!}</p>
</body>
</html>

View File

@@ -0,0 +1,12 @@
<div style="text-align: center; font-weight: bold;">
{{ $company }}
</div>
<br>
<p>{!! $greeting !!}</p>
{{-- <p>An invalid Serial QR Code was scanned:</p> --}}
{{-- <p><strong>Plant:</strong> {{ $plantName }}</p>
<p><strong> {{ $invoiceType }} Invoice Number:</strong> {{ $invoiceNumber }}</p>
<p><strong>Scanned QR Code:</strong> {{ $serial }}</p>
<p>Please ensure the correct format is used.</p> --}}
<p>{!! $wishes !!}</p>

View File

@@ -0,0 +1,137 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Invoice Data Report</title>
<style>
body {
font-family: 'Segoe UI', Arial, sans-serif;
background-color: #f9fafb;
color: #333;
margin: 0;
padding: 0;
}
h2 {
color: #215c98;
text-align: center;
margin-bottom: 20px;
}
table {
border-collapse: collapse;
width: 100%;
font-size: 14px;
white-space: nowrap !important;
}
th, td {
border: 1px solid #020813da;
padding: 8px 10px;
text-align: center;
white-space: nowrap !important;
text-overflow: ellipsis;
}
.td-left {
text-align: left !important;
}
th {
background-color: #215c98;
color: #fff;
white-space: nowrap !important;
}
tr:nth-child(even) {
background-color: #f3f4f6;
}
.status-wentout {
color: #10b981;
font-weight: bold;
}
/* .status-pending {
color: #ef4444;
font-weight: bold;
} */
.status-pending-yellow {
color: #eab308;
font-weight: bold;
}
.status-pending-red {
color: #ef4444;
font-weight: bold;
}
.footer {
text-align: center;
font-size: 13px;
color: #6b7280;
margin-top: 30px;
}
</style>
</head>
<body>
<div style="text-align: center; font-weight: bold;">
{{ $company }}
</div>
<br>
<p>{!! $greeting !!}</p>
<div class="container">
@if(empty($tableData))
<p style="text-align:center;">No invoice data available for this schedule.</p>
@else
<table>
<thead>
<tr>
<th>No</th>
<th>Plant</th>
{{-- <th>Distribution Type</th> --}}
<th>Customer Code</th>
<th>Document Number</th>
<th>Document Date</th>
<th>Customer TradeName</th>
<th>Customer Location</th>
<th>Location</th>
<th>Pending Days</th>
<th>Status</th>
</tr>
</thead>
<tbody>
@foreach ($tableData as $row)
<tr>
<td>{{ $row['no'] }}</td>
<td>{{ $row['plant'] }}</td>
{{-- <td>{{ $row['distribution_type'] }}</td> --}}
<td>{{ $row['customer_code'] }}</td>
<td>{{ $row['document_number'] }}</td>
<td>
@if(!empty($row['document_date']) && $row['document_date'] != '-')
{{ \Carbon\Carbon::parse($row['document_date'])->format('d-M-Y') }}
@else
-
@endif
</td>
{{-- <td>{{ $row['customer_trade_name'] }}</td> --}}
<td class="td-left">{{ $row['customer_trade_name'] }}</td>
<td>{{ $row['customer_location'] }}</td>
<td>{{ $row['location'] }}</td>
{{-- <td class="{{ $row['status'] == 'Went Out' ? 'status-wentout' : ($row['status'] == 'Pending' ? 'status-pending' : '') }}">
{{ $row['status'] }}
</td> --}}
<td>{{ $row['no_of_days_pending'] }}</td>
<td class="{{ $row['status_class'] ?? '' }}">
{{ $row['status'] }}
</td>
</tr>
@endforeach
</tbody>
</table>
@endif
{{-- <p>* <span class="status-pending-red">Pending</span> - Long Days Pending, <span class="status-pending-yellow">Pending</span> - One Day Pending</p> --}}
<p style="margin-top: 20px;">
<strong>* Pending</strong><br>
<span style="display:inline-block; width:11px; height:11px; background-color:#ff4d4d; border:1px solid #ccc; vertical-align:middle; margin-right:6px;"></span>
- Long Days Pending<br>
<span style="display:inline-block; width:9px; height:9px; background-color:#ffd633; border:1px solid #ccc; vertical-align:middle; margin-right:6px;"></span>
- One Day Pending
</p>
<p>{!! $wishes !!}</p>
</div>
</body>
</html>

View File

@@ -0,0 +1 @@
<p>Invoice Number: {{ $invoiceNumber }} has been entered and the page was refreshed.</p>

View File

@@ -0,0 +1,85 @@
<!DOCTYPE html>
<html>
<head>
<title>Invoice Table</title>
<style>
table {
border-collapse: collapse;
width: 60%;
}
th, td {
border: 1px solid #444;
padding: 8px;
text-align: left;
}
th {
background: #f2f2f2;
}
</style>
</head>
<body>
{{-- <h5>{{ $name }}</h5> --}}
{{-- <h5>{!! $name !!}</h5> --}}
<div style="text-align: center; font-weight: bold;">
{{ $company }}
</div>
<br>
<p>{!! $greeting !!}</p>
{{-- <table>
<thead>
<tr>
<th style="text-align: center; border: 1px solid #444; padding: 8px; background: #f2f2f2;">No</th>
<th style="text-align: center; border: 1px solid #444; padding: 8px; background: #f2f2f2;">Plant</th>
<th style="text-align: center; border: 1px solid #444; padding: 8px; background: #f2f2f2;">Line</th>
<th style="text-align: center; border: 1px solid #444; padding: 8px; background: #f2f2f2;">Target Quantity</th>
<th style="text-align: center; border: 1px solid #444; padding: 8px; background: #f2f2f2;">Production Quantity</th>
</tr>
</thead>
<tbody>
@foreach($tableData as $row)
{{-- <tr>
<td>{{ $row['no'] }}</td>
<td>{{ $row['plant'] }}</td>
<td>{{ $row['totalInvoice'] }}</td>
<td>{{ $row['scannedInvoice'] }}</td>
</tr> --}}
{{-- <tr>
<td style="border: 1px solid #444; padding: 8px; text-align: center;">{{ $row['no'] }}</td>
<td style="border: 1px solid #444; padding: 8px; text-align: center; white-space: nowrap;">{{ $row['plant'] }}</td>
<td style="border: 1px solid #444; padding: 8px; text-align: center; white-space: nowrap;">{{ $row['line'] }}</td>
<td style="border: 1px solid #444; padding: 8px; text-align: center;">{{ $row['targetQuantity'] }}</td>
<td style="border: 1px solid #444; padding: 8px; text-align: center;">{{ $row['productionQuantity'] }}</td> --}}
{{-- <td style="border: 1px solid #444; padding: 8px; text-align: center;"></td> --}}
{{-- </tr>
@endforeach
</tbody>
</table> --}}
<table style="table-layout: auto; width: 100%; border-collapse: collapse;">
<thead>
<tr>
<th style="text-align: center; border: 1px solid #444; padding: 8px; background: #f2f2f2;">No</th>
<th style="text-align: center; border: 1px solid #444; padding: 8px; background: #f2f2f2; white-space: nowrap;">Plant</th>
<th style="text-align: center; border: 1px solid #444; padding: 8px; background: #f2f2f2; white-space: nowrap;">Line</th>
<th style="text-align: center; border: 1px solid #444; padding: 8px; background: #f2f2f2;">Target Quantity</th>
<th style="text-align: center; border: 1px solid #444; padding: 8px; background: #f2f2f2;">Line Type</th>
<th style="text-align: center; border: 1px solid #444; padding: 8px; background: #f2f2f2;">Production Quantity</th>
</tr>
</thead>
<tbody>
@foreach($tableData as $row)
<tr>
<td style="border: 1px solid #444; padding: 8px; text-align: center;">{{ $row['no'] }}</td>
<td style="border: 1px solid #444; padding: 8px; text-align: center; white-space: nowrap;">{{ $row['plant'] }}</td>
<td style="border: 1px solid #444; padding: 8px; text-align: center; white-space: nowrap;">{{ $row['line'] }}</td>
<td style="border: 1px solid #444; padding: 8px; text-align: center;">{{ $row['targetQuantity'] }}</td>
<td style="border: 1px solid #444; padding: 8px; text-align: center;">{{ $row['type'] }}</td>
<td style="border: 1px solid #444; padding: 8px; text-align: center;">{{ $row['productionQuantity'] }}</td>
</tr>
@endforeach
</tbody>
</table>
<p>{!! $wishes !!}</p>
</body>
</html>

View File

@@ -0,0 +1,181 @@
{{-- {{ $name }} --}}
{{-- <!DOCTYPE html>
<html>
<head>
<title>Invoice Table</title>
<style>
table {
border-collapse: collapse;
width: 60%;
}
th, td {
border: 1px solid #444;
padding: 8px;
text-align: left;
}
th {
background: #f2f2f2;
}
</style>
</head>
<body>
{{-- <h5>{{ $name }}</h5> --}}
{{-- <h5>{!! $name !!}</h5> --}}
{{-- <div style="text-align: center; font-weight: bold;">
{{ $company }}
</div>
<br>
<h5>{!! $greeting !!}</h5>
<table>
<thead>
<tr>
<th style="text-align: center; border: 1px solid #444; padding: 8px; background: #f2f2f2;">No</th>
<th style="text-align: center; border: 1px solid #444; padding: 8px; background: #f2f2f2;">Plant</th>
<th style="text-align: center; border: 1px solid #444; padding: 8px; background: #f2f2f2;">Total Number Of Invoice</th>
<th style="text-align: center; border: 1px solid #444; padding: 8px; background: #f2f2f2;">Scanned Number Of Invoice</th>
</tr>
</thead>
<tbody>
@foreach($tableData as $row) --}}
{{-- <tr>
<td>{{ $row['no'] }}</td>
<td>{{ $row['plant'] }}</td>
<td>{{ $row['totalInvoice'] }}</td>
<td>{{ $row['scannedInvoice'] }}</td>
</tr> --}}
{{-- <tr>
<td style="border: 1px solid #444; padding: 8px; text-align: center;">{{ $row['no'] }}</td>
<td style="border: 1px solid #444; padding: 8px; text-align: center; white-space: nowrap;">{{ $row['plant'] }}</td>
<td style="border: 1px solid #444; padding: 8px; text-align: center;">{{ $row['totalInvoice'] }}</td>
<td style="border: 1px solid #444; padding: 8px; text-align: center;">{{ $row['scannedInvoice'] }}</td>
</tr>
@endforeach
</tbody>
</table>
<h5>{!! $wishes !!}</h5>
</body>
</html> --}}
<!DOCTYPE html>
<html>
<head>
<title>Invoice Tables</title>
<style>
table {
border-collapse: collapse;
width: 60%;
margin-bottom: 24px;
}
th, td {
border: 1px solid #444;
padding: 8px;
text-align: center;
}
th {
background: #f2f2f2;
}
h4 {
margin-bottom: 8px;
}
</style>
</head>
<body>
<div style="text-align: center; font-weight: bold;">
{{ $company }}
</div>
<br>
<p>{!! $greeting !!}</p>
{{-- Serial Invoice Table --}}
@if(!empty($serialTableData))
<h4>Serial Invoice</h4>
<table>
<thead>
<tr>
<th>No</th>
<th>Plant</th>
<th>Total Number of Invoice</th>
<th>Scanned Number of Invoice</th>
<th>Total Invoice Quantity</th>
<th>Scanned Invoice Quantity</th>
</tr>
</thead>
<tbody>
@foreach($serialTableData as $row)
<tr>
<td>{{ $row['no'] }}</td>
<td style="white-space: nowrap;">{{ $row['plant'] }}</td>
<td>{{ $row['totalInvoice'] }}</td>
<td>{{ $row['scannedInvoice'] }}</td>
<td>{{ $row['totalInvoiceQuan'] }}</td>
<td>{{ $row['scannedInvoiceQuan'] }}</td>
</tr>
@endforeach
</tbody>
</table>
@endif
{{-- Material Invoice Table --}}
@if(!empty($materialTableData))
<h4>Material Invoice</h4>
<table>
<thead>
<tr>
<th>No</th>
<th>Plant</th>
<th>Total Number of Invoice</th>
<th>Scanned Number of Invoice</th>
<th>Total Invoice Quantity</th>
<th>Scanned Invoice Quantity</th>
</tr>
</thead>
<tbody>
@foreach($materialTableData as $row)
<tr>
<td>{{ $row['no'] }}</td>
<td style="white-space: nowrap;">{{ $row['plant'] }}</td>
<td>{{ $row['totalInvoice'] }}</td>
<td>{{ $row['scannedInvoice'] }}</td>
<td>{{ $row['totalInvoiceQuan'] }}</td>
<td>{{ $row['scannedInvoiceQuan'] }}</td>
</tr>
@endforeach
</tbody>
</table>
@endif
{{-- Bundle Invoice Table --}}
@if(!empty($bundleTableData))
<h4>Bundle Invoice</h4>
<table>
<thead>
<tr>
<th>No</th>
<th>Plant</th>
<th>Total Number of Invoice</th>
<th>Scanned Number of Invoice</th>
<th>Total Invoice Quantity</th>
<th>Scanned Invoice Quantity</th>
</tr>
</thead>
<tbody>
@foreach($bundleTableData as $row)
<tr>
<td>{{ $row['no'] }}</td>
<td style="white-space: nowrap;">{{ $row['plant'] }}</td>
<td>{{ $row['totalInvoice'] }}</td>
<td>{{ $row['scannedInvoice'] }}</td>
<td>{{ $row['totalInvoiceQuan'] }}</td>
<td>{{ $row['scannedInvoiceQuan'] }}</td>
</tr>
@endforeach
</tbody>
</table>
@endif
<p>{!! $wishes !!}</p>
</body>
</html>

View File

@@ -0,0 +1,2 @@
{{-- Only include PWA script once --}}
<script src="{{ asset('js/pwa-init.js') }}" defer></script>

View File

@@ -0,0 +1,92 @@
{{-- <!DOCTYPE html>
<html>
<head>
<style>
body { text-align: center; font-family: Arial, sans-serif; }
.qr-container { margin-top: 30px; }
</style>
</head>
<body>
<div class="qr-container">
<img src="data:image/png;base64,{{ $qrCode }}" width="250" height="250">
</div>
</body>
</html> --}}
{{-- <!DOCTYPE html>
<html>
<head>
<style>
@page {
margin: 0;
size: 100mm 100mm;
}
body {
margin: 0;
padding: 0;
width: 100mm;
height: 100mm;
display: flex;
justify-content: center;
align-items: center;
}
img {
width: 100mm;
height: 100mm;
}
</style>
</head>
<body>
<img src="data:image/png;base64,{{ $qrCode }}" />
</body>
</html> --}}
<!DOCTYPE html>
<html>
<head>
<style>
@page {
margin: 0;
size: 100mm 100mm;
}
body {
margin: 0;
padding: 0;
width: 100mm;
height: 100mm;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
font-size: 12px;
font-family: Arial, sans-serif;
}
img {
width: 90mm; /* QR CODE REDUCED TO FIT TEXT */
height: 90mm;
}
.ref-text {
margin-top: 3mm;
font-size: 16px; /* Increased Font Size */
font-weight: bold;
}
</style>
</head>
<body>
<div class="ref-text">
{{ $referenceNumber }}
</div>
<img src="data:image/png;base64,{{ $qrCode }}" />
</body>
</html>

View File

@@ -0,0 +1,94 @@
<!DOCTYPE html>
<html>
<head>
<style>
@media print {
@page {
size: 60mm 14mm;
margin: -1mm;
}
body {
margin: 0;
padding: 0;
width: 60mm;
height: 14mm;
font-family: 'Arial Narrow', Arial, sans-serif;
}
.serial {
text-align: left;
font-family: 'Arial Narrow', Arial, sans-serif;
font-weight: bold;
font-size: 10pt;
}
.desc-row {
font-weight: normal;
font-family: 'Arial Narrow', Arial, sans-serif;
font-size: 8pt;
}
table, td, tr
{
margin: 0;
}
pre
{
margin: 0;
padding-top: 5px;
}
.po-number {
text-align: right;
}
img.qr {
width: 13mm;
height: 13mm;
padding-left: 2mm;
padding-top: 1mm;
}
}
</style>
</head>
<body>
<div id="print-container"></div>
<script>
const copies = {{ $copies }};
const qrBase64 = @json($qrBase64);
const serial = @json($serial);
const productionOrder = @json($productionOrder);
const description = @json($item->description);
const htmlBlock = `
<table>
<tr>
<td>
<img class="qr" src="data:image/png;base64,${qrBase64}" alt="QR" />
</td>
<td>
<pre><span class="serial">${serial}</span><span class="po-number">${productionOrder}</span><br><span class="desc-row">${description}</span></pre>
</td>
</tr>
</table>
`;
const container = document.getElementById("print-container");
for (let i = 0; i < copies; i++) {
container.insertAdjacentHTML("beforeend", htmlBlock);
}
window.onload = function () {
window.print();
setTimeout(() => {
window.close();
}, 500);
};
</script>
</body>
</html>

View File

@@ -0,0 +1,17 @@
@if (isset($data))
<script>
window.filamentData = @js($data)
</script>
@endif
@foreach ($assets as $asset)
@if (! $asset->isLoadedOnRequest())
{{ $asset->getHtml() }}
@endif
@endforeach
<style>
:root {
@foreach ($cssVariables ?? [] as $cssVariableName => $cssVariableValue) --{{ $cssVariableName }}:{{ $cssVariableValue }}; @endforeach
}
</style>

View File

@@ -0,0 +1,64 @@
@php
use Filament\Support\Enums\Alignment;
@endphp
@props([
'actions' => [],
'alignment' => Alignment::Start,
'fullWidth' => false,
])
@php
if (is_array($actions)) {
$actions = array_filter(
$actions,
fn ($action): bool => $action->isVisible(),
);
}
if (! $alignment instanceof Alignment) {
$alignment = filled($alignment) ? (Alignment::tryFrom($alignment) ?? $alignment) : null;
}
$hasActions = false;
$hasSlot = ! \Filament\Support\is_slot_empty($slot);
$actionsAreHtmlable = $actions instanceof \Illuminate\Contracts\Support\Htmlable;
if ($hasSlot) {
$hasActions = true;
} elseif ($actionsAreHtmlable) {
$hasActions = ! \Filament\Support\is_slot_empty($actions);
} else {
$hasActions = filled($actions);
}
@endphp
@if ($hasActions)
<div
{{
$attributes->class([
'fi-ac gap-3',
'flex flex-wrap items-center' => ! $fullWidth,
match ($alignment) {
Alignment::Start, Alignment::Left => 'justify-start',
Alignment::Center => 'justify-center',
Alignment::End, Alignment::Right => 'flex-row-reverse',
Alignment::Between, Alignment::Justify => 'justify-between',
default => $alignment,
} => ! $fullWidth,
'grid grid-cols-[repeat(auto-fit,minmax(0,1fr))]' => $fullWidth,
])
}}
>
@if ($hasSlot)
{{ $slot }}
@elseif ($actionsAreHtmlable)
{{ $actions }}
@else
@foreach ($actions as $action)
{{ $action }}
@endforeach
@endif
</div>
@endif

View File

@@ -0,0 +1,21 @@
@props([
'circular' => true,
'size' => 'md',
])
<img
{{
$attributes
->class([
'fi-avatar object-cover object-center',
'rounded-md' => ! $circular,
'fi-circular rounded-full' => $circular,
match ($size) {
'sm' => 'h-6 w-6',
'md' => 'h-8 w-8',
'lg' => 'h-10 w-10',
default => $size,
},
])
}}
/>

View File

@@ -0,0 +1,238 @@
@php
use Filament\Support\Enums\ActionSize;
use Filament\Support\Enums\IconPosition;
use Filament\Support\Enums\IconSize;
@endphp
@props([
'color' => 'primary',
'deleteButton' => null,
'disabled' => false,
'form' => null,
'formId' => null,
'href' => null,
'icon' => null,
'iconAlias' => null,
'iconPosition' => IconPosition::Before,
'iconSize' => IconSize::Small,
'keyBindings' => null,
'loadingIndicator' => true,
'size' => ActionSize::Medium,
'spaMode' => null,
'tag' => 'span',
'target' => null,
'tooltip' => null,
'type' => 'button',
])
@php
if (! $iconPosition instanceof IconPosition) {
$iconPosition = filled($iconPosition) ? (IconPosition::tryFrom($iconPosition) ?? $iconPosition) : null;
}
if (! $size instanceof ActionSize) {
$size = filled($size) ? (ActionSize::tryFrom($size) ?? $size) : null;
}
if (! $iconSize instanceof IconSize) {
$iconSize = filled($iconSize) ? (IconSize::tryFrom($iconSize) ?? $iconSize) : null;
}
$isDeletable = count($deleteButton?->attributes->getAttributes() ?? []) > 0;
$iconClasses = \Illuminate\Support\Arr::toCssClasses([
'fi-badge-icon h-4 w-4',
match ($iconSize) {
IconSize::Small => 'h-4 w-4',
IconSize::Medium => 'h-5 w-5',
IconSize::Large => 'h-6 w-6',
default => $iconSize,
},
match ($color) {
'gray' => 'text-gray-400 dark:text-gray-500',
default => 'text-custom-500',
},
]);
$iconStyles = \Illuminate\Support\Arr::toCssStyles([
\Filament\Support\get_color_css_variables(
$color,
shades: [500],
alias: 'badge.icon',
) => $color !== 'gray',
]);
$wireTarget = $loadingIndicator ? $attributes->whereStartsWith(['wire:target', 'wire:click'])->filter(fn ($value): bool => filled($value))->first() : null;
$hasLoadingIndicator = filled($wireTarget) || ($type === 'submit' && filled($form));
if ($hasLoadingIndicator) {
$loadingIndicatorTarget = html_entity_decode($wireTarget ?: $form, ENT_QUOTES);
}
$hasTooltip = filled($tooltip);
@endphp
<{{ $tag }}
@if ($tag === 'a')
{{ \Filament\Support\generate_href_html($href, $target === '_blank', $spaMode) }}
@endif
@if ($keyBindings || $hasTooltip)
x-data="{}"
@endif
@if ($keyBindings)
x-bind:id="$id('key-bindings')"
x-mousetrap.global.{{ collect($keyBindings)->map(fn (string $keyBinding): string => str_replace('+', '-', $keyBinding))->implode('.') }}="document.getElementById($el.id).click()"
@endif
@if ($hasTooltip)
x-tooltip="{
content: @js($tooltip),
theme: $store.theme,
}"
@endif
{{
$attributes
->merge([
'disabled' => $disabled,
'form' => $tag === 'button' ? $formId : null,
'type' => $tag === 'button' ? $type : null,
'wire:loading.attr' => $tag === 'button' ? 'disabled' : null,
'wire:target' => ($hasLoadingIndicator && $loadingIndicatorTarget) ? $loadingIndicatorTarget : null,
], escape: false)
->class([
'fi-badge flex items-center justify-center gap-x-1 rounded-md text-xs font-medium ring-1 ring-inset',
'pointer-events-none opacity-70' => $disabled,
match ($size) {
ActionSize::ExtraSmall => 'px-0.5 min-w-[theme(spacing.4)] tracking-tighter',
ActionSize::Small => 'px-1.5 min-w-[theme(spacing.5)] py-0.5 tracking-tight',
ActionSize::Medium, ActionSize::Large, ActionSize::ExtraLarge => 'px-2 min-w-[theme(spacing.6)] py-1',
default => $size,
},
match ($color) {
'gray' => 'bg-gray-50 text-gray-600 ring-gray-600/10 dark:bg-gray-400/10 dark:text-gray-400 dark:ring-gray-400/20',
default => 'fi-color-custom bg-custom-50 text-custom-600 ring-custom-600/10 dark:bg-custom-400/10 dark:text-custom-400 dark:ring-custom-400/30',
},
is_string($color) ? "fi-color-{$color}" : null,
])
->style([
\Filament\Support\get_color_css_variables(
$color,
shades: [
50,
400,
600,
],
alias: 'badge',
) => $color !== 'gray',
])
}}
>
@if ($iconPosition === IconPosition::Before)
@if ($icon)
<x-filament::icon
:attributes="
\Filament\Support\prepare_inherited_attributes(
new \Illuminate\View\ComponentAttributeBag([
'alias' => $iconAlias,
'icon' => $icon,
'wire:loading.remove.delay.' . config('filament.livewire_loading_delay', 'default') => $hasLoadingIndicator,
'wire:target' => $hasLoadingIndicator ? $loadingIndicatorTarget : null,
])
)
->class([$iconClasses])
->style([$iconStyles])
"
/>
@endif
@if ($hasLoadingIndicator)
<x-filament::loading-indicator
:attributes="
\Filament\Support\prepare_inherited_attributes(
new \Illuminate\View\ComponentAttributeBag([
'wire:loading.delay.' . config('filament.livewire_loading_delay', 'default') => '',
'wire:target' => $loadingIndicatorTarget,
])
)
->class([$iconClasses])
->style([$iconStyles])
"
/>
@endif
@endif
<span class="grid">
<span class="truncate">
{{ $slot }}
</span>
</span>
@if ($isDeletable)
<button
type="button"
{{
$deleteButton
->attributes
->except(['label'])
->class([
'fi-badge-delete-button -my-1 -me-2 -ms-1 flex items-center justify-center p-1 outline-none transition duration-75',
match ($color) {
'gray' => 'text-gray-700/50 hover:text-gray-700/75 focus-visible:text-gray-700/75 dark:text-gray-300/50 dark:hover:text-gray-300/75 dark:focus-visible:text-gray-300/75',
default => 'text-custom-700/50 hover:text-custom-700/75 focus-visible:text-custom-700/75 dark:text-custom-300/50 dark:hover:text-custom-300/75 dark:focus-visible:text-custom-300/75',
},
])
->style([
\Filament\Support\get_color_css_variables(
$color,
shades: [300, 700],
alias: 'badge.delete-button',
) => $color !== 'gray',
])
}}
>
<x-filament::icon
alias="badge.delete-button"
icon="heroicon-m-x-mark"
class="h-3.5 w-3.5"
/>
@if (filled($label = $deleteButton->attributes->get('label')))
<span class="sr-only">
{{ $label }}
</span>
@endif
</button>
@elseif ($iconPosition === IconPosition::After)
@if ($icon)
<x-filament::icon
:attributes="
\Filament\Support\prepare_inherited_attributes(
new \Illuminate\View\ComponentAttributeBag([
'alias' => $iconAlias,
'icon' => $icon,
'wire:loading.remove.delay.' . config('filament.livewire_loading_delay', 'default') => $hasLoadingIndicator,
'wire:target' => $hasLoadingIndicator ? $loadingIndicatorTarget : null,
])
)
->class([$iconClasses])
->style([$iconStyles])
"
/>
@endif
@if ($hasLoadingIndicator)
<x-filament::loading-indicator
:attributes="
\Filament\Support\prepare_inherited_attributes(
new \Illuminate\View\ComponentAttributeBag([
'wire:loading.delay.' . config('filament.livewire_loading_delay', 'default') => '',
'wire:target' => $loadingIndicatorTarget,
])
)
->class([$iconClasses])
->style([$iconStyles])
"
/>
@endif
@endif
</{{ $tag }}>

View File

@@ -0,0 +1,50 @@
@props([
'breadcrumbs' => [],
])
@php
$iconClasses = 'fi-breadcrumbs-item-separator flex h-5 w-5 text-gray-400 dark:text-gray-500';
$itemLabelClasses = 'fi-breadcrumbs-item-label text-sm font-medium text-gray-500 dark:text-gray-400';
@endphp
<nav {{ $attributes->class(['fi-breadcrumbs']) }}>
<ol class="fi-breadcrumbs-list flex flex-wrap items-center gap-x-2">
@foreach ($breadcrumbs as $url => $label)
<li class="fi-breadcrumbs-item flex items-center gap-x-2">
@if (! $loop->first)
<x-filament::icon
alias="breadcrumbs.separator"
icon="heroicon-m-chevron-right"
@class([
$iconClasses,
'rtl:hidden',
])
/>
<x-filament::icon
{{-- @deprecated Use `breadcrubs.separator.rtl` instead of `breadcrumbs.separator` for RTL. --}}
:alias="['breadcrumbs.separator.rtl', 'breadcrumbs.separator']"
icon="heroicon-m-chevron-left"
@class([
$iconClasses,
'ltr:hidden',
])
/>
@endif
@if (is_int($url))
<span class="{{ $itemLabelClasses }}">
{{ $label }}
</span>
@else
<a
{{ \Filament\Support\generate_href_html($url) }}
class="{{ $itemLabelClasses }} transition duration-75 hover:text-gray-700 dark:hover:text-gray-200"
>
{{ $label }}
</a>
@endif
</li>
@endforeach
</ol>
</nav>

View File

@@ -0,0 +1,9 @@
<div
{{
$attributes->class([
'fi-btn-group grid grid-flow-col rounded-lg shadow-sm ring-1 ring-gray-950/10 dark:ring-white/20',
])
}}
>
{{ $slot }}
</div>

View File

@@ -0,0 +1,332 @@
@php
use Filament\Support\Enums\ActionSize;
use Filament\Support\Enums\IconPosition;
use Filament\Support\Enums\IconSize;
@endphp
@props([
'badge' => null,
'badgeColor' => 'primary',
'badgeSize' => 'xs',
'color' => 'primary',
'disabled' => false,
'form' => null,
'formId' => null,
'grouped' => false,
'href' => null,
'icon' => null,
'iconAlias' => null,
'iconPosition' => IconPosition::Before,
'iconSize' => null,
'keyBindings' => null,
'labeledFrom' => null,
'labelSrOnly' => false,
'loadingIndicator' => true,
'outlined' => false,
'size' => ActionSize::Medium,
'spaMode' => null,
'tag' => 'button',
'target' => null,
'tooltip' => null,
'type' => 'button',
])
@php
if (! $iconPosition instanceof IconPosition) {
$iconPosition = filled($iconPosition) ? (IconPosition::tryFrom($iconPosition) ?? $iconPosition) : null;
}
if (! $size instanceof ActionSize) {
$size = filled($size) ? (ActionSize::tryFrom($size) ?? $size) : null;
}
$iconSize ??= match ($size) {
ActionSize::ExtraSmall, ActionSize::Small => IconSize::Small,
default => IconSize::Medium,
};
if (! $iconSize instanceof IconSize) {
$iconSize = filled($iconSize) ? (IconSize::tryFrom($iconSize) ?? $iconSize) : null;
}
$buttonClasses = \Illuminate\Support\Arr::toCssClasses([
...[
'fi-btn relative grid-flow-col items-center justify-center font-semibold outline-none transition duration-75 focus-visible:ring-2',
'pointer-events-none opacity-70' => $disabled,
'rounded-lg' => ! $grouped,
'flex-1 [&:nth-child(1_of_.fi-btn)]:rounded-s-lg [&:nth-last-child(1_of_.fi-btn)]:rounded-e-lg [&:not(:nth-child(1_of_.fi-btn))]:shadow-[-1px_0_0_0_theme(colors.gray.200)] [&:not(:nth-last-child(1_of_.fi-btn))]:me-px dark:[&:not(:nth-child(1_of_.fi-btn))]:shadow-[-1px_0_0_0_theme(colors.white/20%)]' => $grouped,
'cursor-pointer' => $tag === 'label',
match ($color) {
'gray' => null,
default => 'fi-color-custom',
},
// @deprecated `fi-btn-color-*` has been replaced by `fi-color-*` and `fi-color-custom`.
is_string($color) ? "fi-btn-color-{$color}" : null,
is_string($color) ? "fi-color-{$color}" : null,
($size instanceof ActionSize) ? "fi-size-{$size->value}" : null,
// @deprecated `fi-btn-size-*` has been replaced by `fi-size-*`.
($size instanceof ActionSize) ? "fi-btn-size-{$size->value}" : null,
match ($size) {
ActionSize::ExtraSmall => 'gap-1 px-2 py-1.5 text-xs',
ActionSize::Small => 'gap-1 px-2.5 py-1.5 text-sm',
ActionSize::Medium => 'gap-1.5 px-3 py-2 text-sm',
ActionSize::Large => 'gap-1.5 px-3.5 py-2.5 text-sm',
ActionSize::ExtraLarge => 'gap-1.5 px-4 py-3 text-sm',
default => $size,
},
'hidden' => $labeledFrom,
match ($labeledFrom) {
'sm' => 'sm:inline-grid',
'md' => 'md:inline-grid',
'lg' => 'lg:inline-grid',
'xl' => 'xl:inline-grid',
'2xl' => '2xl:inline-grid',
default => 'inline-grid',
},
],
...(
$outlined ?
[
'fi-btn-outlined ring-1',
match ($color) {
'gray' => 'text-gray-950 ring-gray-300 hover:bg-gray-400/10 focus-visible:ring-gray-400/40 dark:text-white dark:ring-gray-700',
default => 'text-custom-600 ring-custom-600 hover:bg-custom-400/10 dark:text-custom-400 dark:ring-custom-500',
},
] :
[
'shadow-sm' => ! $grouped,
'bg-white text-gray-950 hover:bg-gray-50 dark:bg-white/5 dark:text-white dark:hover:bg-white/10' => ($color === 'gray') || ($tag === 'label'),
'ring-1 ring-gray-950/10 dark:ring-white/20' => (($color === 'gray') || ($tag === 'label')) && (! $grouped),
'bg-custom-600 text-white hover:bg-custom-500 focus-visible:ring-custom-500/50 dark:bg-custom-500 dark:hover:bg-custom-400 dark:focus-visible:ring-custom-400/50' => ($color !== 'gray') && ($tag !== 'label'),
'[input:checked+&]:bg-custom-600 [input:checked+&]:text-white [input:checked+&]:ring-0 [input:checked+&]:hover:bg-custom-500 dark:[input:checked+&]:bg-custom-500 dark:[input:checked+&]:hover:bg-custom-400 [input:checked:focus-visible+&]:ring-custom-500/50 dark:[input:checked:focus-visible+&]:ring-custom-400/50 [input:focus-visible+&]:z-10 [input:focus-visible+&]:ring-2 [input:focus-visible+&]:ring-gray-950/10 dark:[input:focus-visible+&]:ring-white/20' => ($color !== 'gray') && ($tag === 'label'),
'[input:checked+&]:bg-gray-400 [input:checked+&]:text-white [input:checked+&]:ring-0 [input:checked+&]:hover:bg-gray-300 dark:[input:checked+&]:bg-gray-600 dark:[input:checked+&]:hover:bg-gray-500' => ($color === 'gray'),
]
),
]);
$buttonStyles = \Illuminate\Support\Arr::toCssStyles([
\Filament\Support\get_color_css_variables(
$color,
shades: [400, 500, 600],
alias: 'button',
) => $color !== 'gray',
]);
$iconClasses = \Illuminate\Support\Arr::toCssClasses([
'fi-btn-icon transition duration-75',
match ($iconSize) {
IconSize::Small => 'h-4 w-4',
IconSize::Medium => 'h-5 w-5',
IconSize::Large => 'h-6 w-6',
default => $iconSize,
},
'text-gray-400 dark:text-gray-500' => ($color === 'gray') || ($tag === 'label'),
'text-white' => ($color !== 'gray') && ($tag !== 'label') && (! $outlined),
'[:checked+*>&]:text-white' => $tag === 'label',
]);
$badgeContainerClasses = 'fi-btn-badge-ctn absolute start-full top-0 z-[1] w-max -translate-x-1/2 -translate-y-1/2 rounded-md bg-white dark:bg-gray-900 rtl:translate-x-1/2';
$labelClasses = \Illuminate\Support\Arr::toCssClasses([
'fi-btn-label',
'sr-only' => $labelSrOnly,
]);
$wireTarget = $loadingIndicator ? $attributes->whereStartsWith(['wire:target', 'wire:click'])->filter(fn ($value): bool => filled($value))->first() : null;
$hasFormProcessingLoadingIndicator = $type === 'submit' && filled($form);
$hasLoadingIndicator = filled($wireTarget) || $hasFormProcessingLoadingIndicator;
if ($hasLoadingIndicator) {
$loadingIndicatorTarget = html_entity_decode($wireTarget ?: $form, ENT_QUOTES);
}
$hasTooltip = filled($tooltip);
@endphp
@if ($labeledFrom)
<x-filament::icon-button
:badge="$badge"
:badge-color="$badgeColor"
:color="$color"
:disabled="$disabled"
:form="$form"
:form-id="$formId"
:href="$href"
:icon="$icon"
:icon-alias="$iconAlias"
:icon-size="$iconSize"
:key-bindings="$keyBindings"
:label="$slot"
:size="$size"
:tag="$tag"
:target="$target"
:tooltip="$tooltip"
:type="$type"
:class="
match ($labeledFrom) {
'sm' => 'sm:hidden',
'md' => 'md:hidden',
'lg' => 'lg:hidden',
'xl' => 'xl:hidden',
'2xl' => '2xl:hidden',
default => 'hidden',
}
"
:attributes="\Filament\Support\prepare_inherited_attributes($attributes)"
/>
@endif
<{{ $tag }}
@if ($tag === 'a')
{{ \Filament\Support\generate_href_html($href, $target === '_blank', $spaMode) }}
@endif
@if (($keyBindings || $hasTooltip) && (! $hasFormProcessingLoadingIndicator))
x-data="{}"
@endif
@if ($keyBindings)
x-bind:id="$id('key-bindings')"
x-mousetrap.global.{{ collect($keyBindings)->map(fn (string $keyBinding): string => str_replace('+', '-', $keyBinding))->implode('.') }}="document.getElementById($el.id).click()"
@endif
@if ($hasTooltip)
x-tooltip="{
content: @js($tooltip),
theme: $store.theme,
}"
@endif
@if ($hasFormProcessingLoadingIndicator)
x-data="{
form: null,
isProcessing: false,
processingMessage: null,
}"
x-init="
form = $el.closest('form')
form?.addEventListener('form-processing-started', (event) => {
isProcessing = true
processingMessage = event.detail.message
})
form?.addEventListener('form-processing-finished', () => {
isProcessing = false
})
"
x-bind:class="{ 'enabled:opacity-70 enabled:cursor-wait': isProcessing }"
@endif
{{
$attributes
->merge([
'disabled' => $disabled,
'form' => $formId,
'type' => $tag === 'button' ? $type : null,
'wire:loading.attr' => $tag === 'button' ? 'disabled' : null,
'wire:target' => ($hasLoadingIndicator && $loadingIndicatorTarget) ? $loadingIndicatorTarget : null,
'x-bind:disabled' => $hasFormProcessingLoadingIndicator ? 'isProcessing' : null,
], escape: false)
->class([$buttonClasses])
->style([$buttonStyles])
}}
>
@if ($iconPosition === IconPosition::Before)
@if ($icon)
<x-filament::icon
:attributes="
\Filament\Support\prepare_inherited_attributes(
new \Illuminate\View\ComponentAttributeBag([
'alias' => $iconAlias,
'icon' => $icon,
'wire:loading.remove.delay.' . config('filament.livewire_loading_delay', 'default') => $hasLoadingIndicator,
'wire:target' => $hasLoadingIndicator ? $loadingIndicatorTarget : null,
])
)->class([$iconClasses])
"
/>
@endif
@if ($hasLoadingIndicator)
<x-filament::loading-indicator
:attributes="
\Filament\Support\prepare_inherited_attributes(
new \Illuminate\View\ComponentAttributeBag([
'wire:loading.delay.' . config('filament.livewire_loading_delay', 'default') => '',
'wire:target' => $loadingIndicatorTarget,
])
)->class([$iconClasses])
"
/>
@endif
@if ($hasFormProcessingLoadingIndicator)
<x-filament::loading-indicator
x-cloak="x-cloak"
x-show="isProcessing"
:class="$iconClasses"
/>
@endif
@endif
<span
@if ($hasFormProcessingLoadingIndicator)
x-show="! isProcessing"
@endif
class="{{ $labelClasses }}"
>
{{ $slot }}
</span>
@if ($hasFormProcessingLoadingIndicator)
<span
x-cloak
x-show="isProcessing"
x-text="processingMessage"
class="{{ $labelClasses }}"
></span>
@endif
@if ($iconPosition === IconPosition::After)
@if ($icon)
<x-filament::icon
:attributes="
\Filament\Support\prepare_inherited_attributes(
new \Illuminate\View\ComponentAttributeBag([
'alias' => $iconAlias,
'icon' => $icon,
'wire:loading.remove.delay.' . config('filament.livewire_loading_delay', 'default') => $hasLoadingIndicator,
'wire:target' => $hasLoadingIndicator ? $loadingIndicatorTarget : null,
])
)->class([$iconClasses])
"
/>
@endif
@if ($hasLoadingIndicator)
<x-filament::loading-indicator
:attributes="
\Filament\Support\prepare_inherited_attributes(
new \Illuminate\View\ComponentAttributeBag([
'wire:loading.delay.' . config('filament.livewire_loading_delay', 'default') => '',
'wire:target' => $loadingIndicatorTarget,
])
)->class([$iconClasses])
"
/>
@endif
@if ($hasFormProcessingLoadingIndicator)
<x-filament::loading-indicator
x-cloak="x-cloak"
x-show="isProcessing"
:class="$iconClasses"
/>
@endif
@endif
@if (filled($badge))
<div class="{{ $badgeContainerClasses }}">
<x-filament::badge :color="$badgeColor" :size="$badgeSize">
{{ $badge }}
</x-filament::badge>
</div>
@endif
</{{ $tag }}>

View File

@@ -0,0 +1,3 @@
<x-filament::section>
{{ $slot }}
</x-filament::section>

View File

@@ -0,0 +1,71 @@
@php
use Filament\Support\Enums\IconSize;
@endphp
@props([
'color' => 'gray',
'icon' => null,
'iconSize' => IconSize::Medium,
'tag' => 'div',
])
<{{ $tag }}
{{
$attributes
->class([
'fi-dropdown-header flex w-full gap-2 p-3 text-sm',
match ($color) {
'gray' => null,
default => 'fi-color-custom',
},
// @deprecated `fi-dropdown-header-color-*` has been replaced by `fi-color-*` and `fi-color-custom`.
is_string($color) ? "fi-dropdown-header-color-{$color}" : null,
is_string($color) ? "fi-color-{$color}" : null,
])
}}
>
@if (filled($icon))
<x-filament::icon
:icon="$icon"
@class([
'fi-dropdown-header-icon',
match ($iconSize) {
IconSize::Small, 'sm' => 'h-4 w-4',
IconSize::Medium, 'md' => 'h-5 w-5',
IconSize::Large, 'lg' => 'h-6 w-6',
default => $iconSize,
},
match ($color) {
'gray' => 'text-gray-400 dark:text-gray-500',
default => 'text-custom-500 dark:text-custom-400',
},
])
@style([
\Filament\Support\get_color_css_variables(
$color,
shades: [400, 500],
alias: 'dropdown.header.icon',
) => $color !== 'gray',
])
/>
@endif
<span
@class([
'fi-dropdown-header-label flex-1 truncate text-start',
match ($color) {
'gray' => 'text-gray-700 dark:text-gray-200',
default => 'text-custom-600 dark:text-custom-400',
},
])
@style([
\Filament\Support\get_color_css_variables(
$color,
shades: [400, 600],
alias: 'dropdown.header.label',
) => $color !== 'gray',
])
>
{{ $slot }}
</span>
</{{ $tag }}>

View File

@@ -0,0 +1,87 @@
@props([
'availableHeight' => null,
'availableWidth' => null,
'flip' => true,
'maxHeight' => null,
'offset' => 8,
'placement' => null,
'shift' => false,
'size' => false,
'sizePadding' => 16,
'teleport' => false,
'trigger' => null,
'width' => null,
])
@php
use Filament\Support\Enums\MaxWidth;
$sizeConfig = collect([
'availableHeight' => $availableHeight,
'availableWidth' => $availableWidth,
'padding' => $sizePadding,
])->filter()->toJson();
@endphp
<div
x-data="{
toggle: function (event) {
$refs.panel.toggle(event)
},
open: function (event) {
$refs.panel.open(event)
},
close: function (event) {
$refs.panel.close(event)
},
}"
{{ $attributes->class(['fi-dropdown']) }}
>
<div
x-on:click="toggle"
{{ $trigger->attributes->class(['fi-dropdown-trigger flex cursor-pointer']) }}
>
{{ $trigger }}
</div>
@if (! \Filament\Support\is_slot_empty($slot))
<div
x-cloak
x-float{{ $placement ? ".placement.{$placement}" : '' }}{{ $size ? '.size' : '' }}{{ $flip ? '.flip' : '' }}{{ $shift ? '.shift' : '' }}{{ $teleport ? '.teleport' : '' }}{{ $offset ? '.offset' : '' }}="{ offset: {{ $offset }}, {{ $size ? ('size: ' . $sizeConfig) : '' }} }"
x-ref="panel"
x-transition:enter-start="opacity-0"
x-transition:leave-end="opacity-0"
@if ($attributes->has('wire:key'))
wire:ignore.self
wire:key="{{ $attributes->get('wire:key') }}.panel"
@endif
@class([
'fi-dropdown-panel absolute z-10 w-screen divide-y divide-gray-100 rounded-lg bg-white shadow-lg ring-1 ring-gray-950/5 transition dark:divide-white/5 dark:bg-gray-900 dark:ring-white/10',
match ($width) {
// These max width classes need to be `!important` otherwise they will be usurped by the Floating UI "size" middleware.
MaxWidth::ExtraSmall, 'xs' => '!max-w-xs',
MaxWidth::Small, 'sm' => '!max-w-sm',
MaxWidth::Medium, 'md' => '!max-w-md',
MaxWidth::Large, 'lg' => '!max-w-lg',
MaxWidth::ExtraLarge, 'xl' => '!max-w-xl',
MaxWidth::TwoExtraLarge, '2xl' => '!max-w-2xl',
MaxWidth::ThreeExtraLarge, '3xl' => '!max-w-3xl',
MaxWidth::FourExtraLarge, '4xl' => '!max-w-4xl',
MaxWidth::FiveExtraLarge, '5xl' => '!max-w-5xl',
MaxWidth::SixExtraLarge, '6xl' => '!max-w-6xl',
MaxWidth::SevenExtraLarge, '7xl' => '!max-w-7xl',
null => '!max-w-[14rem]',
default => $width,
},
'overflow-y-auto' => $maxHeight || $size,
])
@style([
"max-height: {$maxHeight}" => $maxHeight,
])
>
{{ $slot }}
</div>
@endif
</div>

View File

@@ -0,0 +1,3 @@
<div {{ $attributes->class(['fi-dropdown-list p-1']) }}>
{{ $slot }}
</div>

View File

@@ -0,0 +1,275 @@
@php
use Filament\Support\Enums\IconSize;
@endphp
@props([
'badge' => null,
'badgeColor' => null,
'badgeTooltip' => null,
'color' => 'gray',
'disabled' => false,
'href' => null,
'icon' => null,
'iconAlias' => null,
'iconColor' => null,
'iconSize' => IconSize::Medium,
'image' => null,
'keyBindings' => null,
'loadingIndicator' => true,
'spaMode' => null,
'tag' => 'button',
'target' => null,
'tooltip' => null,
])
@php
$buttonClasses = \Illuminate\Support\Arr::toCssClasses([
'fi-dropdown-list-item flex w-full items-center gap-2 whitespace-nowrap rounded-md p-2 text-sm transition-colors duration-75 outline-none disabled:pointer-events-none disabled:opacity-70',
'pointer-events-none opacity-70' => $disabled,
match ($color) {
'gray' => 'hover:bg-gray-50 focus-visible:bg-gray-50 dark:hover:bg-white/5 dark:focus-visible:bg-white/5',
default => 'fi-color-custom hover:bg-custom-50 focus-visible:bg-custom-50 dark:hover:bg-custom-400/10 dark:focus-visible:bg-custom-400/10',
},
// @deprecated `fi-dropdown-list-item-color-*` has been replaced by `fi-color-*` and `fi-color-custom`.
is_string($color) ? "fi-dropdown-list-item-color-{$color}" : null,
is_string($color) ? "fi-color-{$color}" : null,
]);
$buttonStyles = \Illuminate\Support\Arr::toCssStyles([
\Filament\Support\get_color_css_variables(
$color,
shades: [50, 400],
alias: 'dropdown.list.item',
) => $color !== 'gray',
]);
$iconColor ??= $color;
$iconClasses = \Illuminate\Support\Arr::toCssClasses([
'fi-dropdown-list-item-icon',
match ($iconSize) {
IconSize::Small, 'sm' => 'h-4 w-4',
IconSize::Medium, 'md' => 'h-5 w-5',
IconSize::Large, 'lg' => 'h-6 w-6',
default => $iconSize,
},
match ($iconColor) {
'gray' => 'text-gray-400 dark:text-gray-500',
default => 'text-custom-500 dark:text-custom-400',
},
]);
$iconStyles = \Illuminate\Support\Arr::toCssStyles([
\Filament\Support\get_color_css_variables(
$iconColor,
shades: [400, 500],
alias: 'dropdown.list.item.icon',
) => $iconColor !== 'gray',
]);
$imageClasses = 'fi-dropdown-list-item-image h-5 w-5 rounded-full bg-cover bg-center';
$labelClasses = \Illuminate\Support\Arr::toCssClasses([
'fi-dropdown-list-item-label flex-1 truncate text-start',
match ($color) {
'gray' => 'text-gray-700 dark:text-gray-200',
default => 'text-custom-600 dark:text-custom-400 ',
},
]);
$labelStyles = \Illuminate\Support\Arr::toCssStyles([
\Filament\Support\get_color_css_variables(
$color,
shades: [400, 600],
alias: 'dropdown.list.item.label',
) => $color !== 'gray',
]);
$wireTarget = $loadingIndicator ? $attributes->whereStartsWith(['wire:target', 'wire:click'])->filter(fn ($value): bool => filled($value))->first() : null;
$hasLoadingIndicator = filled($wireTarget);
if ($hasLoadingIndicator) {
$loadingIndicatorTarget = html_entity_decode($wireTarget, ENT_QUOTES);
}
$hasTooltip = filled($tooltip);
@endphp
@if ($tag === 'button')
<button
@if ($keyBindings || $hasTooltip)
x-data="{}"
@endif
@if ($keyBindings)
x-bind:id="$id('key-bindings')"
x-mousetrap.global.{{ collect($keyBindings)->map(fn (string $keyBinding): string => str_replace('+', '-', $keyBinding))->implode('.') }}="document.getElementById($el.id).click()"
@endif
@if ($hasTooltip)
x-tooltip="{
content: @js($tooltip),
theme: $store.theme,
}"
@endif
{{
$attributes
->merge([
'disabled' => $disabled,
'type' => 'button',
'wire:loading.attr' => 'disabled',
'wire:target' => $hasLoadingIndicator ? $loadingIndicatorTarget : null,
], escape: false)
->class([$buttonClasses])
->style([$buttonStyles])
}}
>
@if ($icon)
<x-filament::icon
:attributes="
\Filament\Support\prepare_inherited_attributes(
new \Illuminate\View\ComponentAttributeBag([
'alias' => $iconAlias,
'icon' => $icon,
'wire:loading.remove.delay.' . config('filament.livewire_loading_delay', 'default') => $hasLoadingIndicator,
'wire:target' => $hasLoadingIndicator ? $loadingIndicatorTarget : null,
])
)
->class([$iconClasses])
->style([$iconStyles])
"
/>
@endif
@if ($image)
<div
class="{{ $imageClasses }}"
style="background-image: url('{{ $image }}')"
></div>
@endif
@if ($hasLoadingIndicator)
<x-filament::loading-indicator
:attributes="
\Filament\Support\prepare_inherited_attributes(
new \Illuminate\View\ComponentAttributeBag([
'wire:loading.delay.' . config('filament.livewire_loading_delay', 'default') => '',
'wire:target' => $loadingIndicatorTarget,
])
)
->class([$iconClasses])
->style([$iconStyles])
"
/>
@endif
<span class="{{ $labelClasses }}" style="{{ $labelStyles }}">
{{ $slot }}
</span>
@if (filled($badge))
<x-filament::badge
:color="$badgeColor"
size="sm"
:tooltip="$badgeTooltip"
>
{{ $badge }}
</x-filament::badge>
@endif
</button>
@elseif ($tag === 'a')
<a
{{ \Filament\Support\generate_href_html($href, $target === '_blank', $spaMode) }}
@if ($keyBindings || $hasTooltip)
x-data="{}"
@endif
@if ($keyBindings)
x-bind:id="$id('key-bindings')"
x-mousetrap.global.{{ collect($keyBindings)->map(fn (string $keyBinding): string => str_replace('+', '-', $keyBinding))->implode('.') }}="document.getElementById($el.id).click()"
@endif
@if ($hasTooltip)
x-tooltip="{
content: @js($tooltip),
theme: $store.theme,
}"
@endif
{{
$attributes
->class([$buttonClasses])
->style([$buttonStyles])
}}
>
@if ($icon)
<x-filament::icon
:alias="$iconAlias"
:icon="$icon"
:class="$iconClasses"
:style="$iconStyles"
/>
@endif
@if ($image)
<div
class="{{ $imageClasses }}"
style="background-image: url('{{ $image }}')"
></div>
@endif
<span class="{{ $labelClasses }}" style="{{ $labelStyles }}">
{{ $slot }}
</span>
@if (filled($badge))
<x-filament::badge :color="$badgeColor" size="sm">
{{ $badge }}
</x-filament::badge>
@endif
</a>
@elseif ($tag === 'form')
<form
{{ $attributes->only(['action', 'class', 'method', 'wire:submit']) }}
>
@csrf
<button
@if ($keyBindings || $hasTooltip)
x-data="{}"
@endif
@if ($keyBindings)
x-bind:id="$id('key-bindings')"
x-mousetrap.global.{{ collect($keyBindings)->map(fn (string $keyBinding): string => str_replace('+', '-', $keyBinding))->implode('.') }}="document.getElementById($el.id).click()"
@endif
@if ($hasTooltip)
x-tooltip="{
content: @js($tooltip),
theme: $store.theme,
}"
@endif
type="submit"
{{
$attributes
->except(['action', 'class', 'method', 'wire:submit'])
->class([$buttonClasses])
->style([$buttonStyles])
}}
>
@if ($icon)
<x-filament::icon
:alias="$iconAlias"
:icon="$icon"
:class="$iconClasses"
:style="$iconStyles"
/>
@endif
<span class="{{ $labelClasses }}" style="{{ $labelStyles }}">
{{ $slot }}
</span>
@if (filled($badge))
<x-filament::badge :color="$badgeColor" size="sm">
{{ $badge }}
</x-filament::badge>
@endif
</button>
</form>
@endif

View File

@@ -0,0 +1,25 @@
@props([
'label' => null,
'labelHidden' => false,
])
<fieldset
{{
$attributes->class([
'fi-fieldset rounded-xl border border-gray-200 p-6 dark:border-white/10',
])
}}
>
@if (filled($label))
<legend
@class([
'-ms-2 px-2 text-sm font-medium leading-6 text-gray-950 dark:text-white',
'sr-only' => $labelHidden,
])
>
{{ $label }}
</legend>
@endif
{{ $slot }}
</fieldset>

View File

@@ -0,0 +1,62 @@
@props([
'default' => 1,
'sm' => null,
'md' => null,
'lg' => null,
'xl' => null,
'twoXl' => null,
'defaultStart' => null,
'smStart' => null,
'mdStart' => null,
'lgStart' => null,
'xlStart' => null,
'twoXlStart' => null,
'hidden' => false,
])
@php
$getSpanValue = function ($span): string {
if ($span === 'full') {
return '1 / -1';
}
return "span {$span} / span {$span}";
};
@endphp
<div
{{
$attributes
->class([
'hidden' => $hidden || $default === 'hidden',
'col-[--col-span-default]' => $default && (! $hidden),
'sm:col-[--col-span-sm]' => $sm && (! $hidden),
'md:col-[--col-span-md]' => $md && (! $hidden),
'lg:col-[--col-span-lg]' => $lg && (! $hidden),
'xl:col-[--col-span-xl]' => $xl && (! $hidden),
'2xl:col-[--col-span-2xl]' => $twoXl && (! $hidden),
'col-start-[--col-start-default]' => $defaultStart && (! $hidden),
'sm:col-start-[--col-start-sm]' => $smStart && (! $hidden),
'md:col-start-[--col-start-md]' => $mdStart && (! $hidden),
'lg:col-start-[--col-start-lg]' => $lgStart && (! $hidden),
'xl:col-start-[--col-start-xl]' => $xlStart && (! $hidden),
'2xl:col-start-[--col-start-2xl]' => $twoXlStart && (! $hidden),
])
->style([
"--col-span-default: {$getSpanValue($default)}" => $default,
"--col-span-sm: {$getSpanValue($sm)}" => $sm,
"--col-span-md: {$getSpanValue($md)}" => $md,
"--col-span-lg: {$getSpanValue($lg)}" => $lg,
"--col-span-xl: {$getSpanValue($xl)}" => $xl,
"--col-span-2xl: {$getSpanValue($twoXl)}" => $twoXl,
"--col-start-default: {$defaultStart}" => $defaultStart,
"--col-start-sm: {$smStart}" => $smStart,
"--col-start-md: {$mdStart}" => $mdStart,
"--col-start-lg: {$lgStart}" => $lgStart,
"--col-start-xl: {$xlStart}" => $xlStart,
"--col-start-2xl: {$twoXlStart}" => $twoXlStart,
])
}}
>
{{ $slot }}
</div>

View File

@@ -0,0 +1,53 @@
@props([
'isGrid' => true,
'default' => 1,
'direction' => 'row',
'sm' => null,
'md' => null,
'lg' => null,
'xl' => null,
'twoXl' => null,
])
<div
{{
$attributes
->class([
'grid' => $isGrid && $direction === 'row',
'grid-cols-[--cols-default]' => $default && ($direction === 'row'),
'columns-[--cols-default]' => $default && ($direction === 'column'),
'sm:grid-cols-[--cols-sm]' => $sm && ($direction === 'row'),
'sm:columns-[--cols-sm]' => $sm && ($direction === 'column'),
'md:grid-cols-[--cols-md]' => $md && ($direction === 'row'),
'md:columns-[--cols-md]' => $md && ($direction === 'column'),
'lg:grid-cols-[--cols-lg]' => $lg && ($direction === 'row'),
'lg:columns-[--cols-lg]' => $lg && ($direction === 'column'),
'xl:grid-cols-[--cols-xl]' => $xl && ($direction === 'row'),
'xl:columns-[--cols-xl]' => $xl && ($direction === 'column'),
'2xl:grid-cols-[--cols-2xl]' => $twoXl && ($direction === 'row'),
'2xl:columns-[--cols-2xl]' => $twoXl && ($direction === 'column'),
])
->style(
match ($direction) {
'column' => [
"--cols-default: {$default}" => $default,
"--cols-sm: {$sm}" => $sm,
"--cols-md: {$md}" => $md,
"--cols-lg: {$lg}" => $lg,
"--cols-xl: {$xl}" => $xl,
"--cols-2xl: {$twoXl}" => $twoXl,
],
'row' => [
"--cols-default: repeat({$default}, minmax(0, 1fr))" => $default,
"--cols-sm: repeat({$sm}, minmax(0, 1fr))" => $sm,
"--cols-md: repeat({$md}, minmax(0, 1fr))" => $md,
"--cols-lg: repeat({$lg}, minmax(0, 1fr))" => $lg,
"--cols-xl: repeat({$xl}, minmax(0, 1fr))" => $xl,
"--cols-2xl: repeat({$twoXl}, minmax(0, 1fr))" => $twoXl,
],
},
)
}}
>
{{ $slot }}
</div>

View File

@@ -0,0 +1,242 @@
@php
use Filament\Support\Enums\ActionSize;
use Filament\Support\Enums\IconSize;
@endphp
@props([
'badge' => null,
'badgeColor' => 'primary',
'badgeSize' => 'xs',
'color' => 'primary',
'disabled' => false,
'form' => null,
'formId' => null,
'href' => null,
'icon' => null,
'iconAlias' => null,
'iconSize' => null,
'keyBindings' => null,
'label' => null,
'loadingIndicator' => true,
'size' => ActionSize::Medium,
'spaMode' => null,
'tag' => 'button',
'target' => null,
'tooltip' => null,
'type' => 'button',
])
@php
if (! $size instanceof ActionSize) {
$size = filled($size) ? (ActionSize::tryFrom($size) ?? $size) : null;
}
$iconSize ??= match ($size) {
ActionSize::ExtraSmall => IconSize::Small,
ActionSize::Small, ActionSize::Medium => IconSize::Medium,
ActionSize::Large, ActionSize::ExtraLarge => IconSize::Large,
default => IconSize::Medium,
};
if (! $iconSize instanceof IconSize) {
$iconSize = filled($iconSize) ? (IconSize::tryFrom($iconSize) ?? $iconSize) : null;
}
$buttonClasses = \Illuminate\Support\Arr::toCssClasses([
'fi-icon-btn relative flex items-center justify-center rounded-lg outline-none transition duration-75 focus-visible:ring-2',
'pointer-events-none opacity-70' => $disabled,
...match ($size) {
ActionSize::ExtraSmall => [
match ($iconSize) {
IconSize::Small => '-m-1.5',
IconSize::Medium => '-m-1',
IconSize::Large => '-m-0.5',
},
'h-7 w-7',
],
ActionSize::Small => [
match ($iconSize) {
IconSize::Small => '-m-2',
IconSize::Medium => '-m-1.5',
IconSize::Large => '-m-1',
},
'h-8 w-8',
],
ActionSize::Medium => [
match ($iconSize) {
IconSize::Small => '-m-2.5',
IconSize::Medium => '-m-2',
IconSize::Large => '-m-1.5',
},
'h-9 w-9',
],
ActionSize::Large => [
match ($iconSize) {
IconSize::Small => '-m-3',
IconSize::Medium => '-m-2.5',
IconSize::Large => '-m-2',
},
'h-10 w-10',
],
ActionSize::ExtraLarge => [
match ($iconSize) {
IconSize::Small => '-m-3.5',
IconSize::Medium => '-m-3',
IconSize::Large => '-m-2.5',
},
'h-11 w-11',
],
},
match ($color) {
'gray' => 'text-gray-400 hover:text-gray-500 focus-visible:ring-primary-600 dark:text-gray-500 dark:hover:text-gray-400 dark:focus-visible:ring-primary-500',
default => 'fi-color-custom text-custom-500 hover:text-custom-600 focus-visible:ring-custom-600 dark:text-custom-400 dark:hover:text-custom-300 dark:focus-visible:ring-custom-500',
},
is_string($color) ? "fi-color-{$color}" : null,
]);
$buttonStyles = \Filament\Support\get_color_css_variables(
$color,
shades: [300, 400, 500, 600],
alias: 'icon-button',
);
$iconClasses = \Illuminate\Support\Arr::toCssClasses([
'fi-icon-btn-icon',
match ($iconSize) {
IconSize::Small => 'h-4 w-4',
IconSize::Medium => 'h-5 w-5',
IconSize::Large => 'h-6 w-6',
default => $iconSize,
},
]);
$badgeContainerClasses = 'fi-icon-btn-badge-ctn absolute start-full top-1 z-[1] w-max -translate-x-1/2 -translate-y-1/2 rounded-md bg-white dark:bg-gray-900 rtl:translate-x-1/2';
$wireTarget = $loadingIndicator ? $attributes->whereStartsWith(['wire:target', 'wire:click'])->filter(fn ($value): bool => filled($value))->first() : null;
$hasLoadingIndicator = filled($wireTarget) || ($type === 'submit' && filled($form));
if ($hasLoadingIndicator) {
$loadingIndicatorTarget = html_entity_decode($wireTarget ?: $form, ENT_QUOTES);
}
$hasTooltip = filled($tooltip);
@endphp
@if ($tag === 'button')
<button
@if ($keyBindings || $hasTooltip)
x-data="{}"
@endif
@if ($keyBindings)
x-bind:id="$id('key-bindings')"
x-mousetrap.global.{{ collect($keyBindings)->map(fn (string $keyBinding): string => str_replace('+', '-', $keyBinding))->implode('.') }}="document.getElementById($el.id).click()"
@endif
@if ($hasTooltip)
x-tooltip="{
content: @js($tooltip),
theme: $store.theme,
}"
@endif
{{
$attributes
->merge([
'disabled' => $disabled,
'form' => $formId,
'type' => $type,
'wire:loading.attr' => 'disabled',
'wire:target' => ($hasLoadingIndicator && $loadingIndicatorTarget) ? $loadingIndicatorTarget : null,
], escape: false)
->merge([
'title' => $hasTooltip ? null : $label,
], escape: true)
->class([$buttonClasses])
->style([$buttonStyles])
}}
>
@if ($label)
<span class="sr-only">
{{ $label }}
</span>
@endif
<x-filament::icon
:attributes="
\Filament\Support\prepare_inherited_attributes(
new \Illuminate\View\ComponentAttributeBag([
'alias' => $iconAlias,
'icon' => $icon,
'wire:loading.remove.delay.' . config('filament.livewire_loading_delay', 'default') => $hasLoadingIndicator,
'wire:target' => $hasLoadingIndicator ? $loadingIndicatorTarget : null,
])
)->class([$iconClasses])
"
/>
@if ($hasLoadingIndicator)
<x-filament::loading-indicator
:attributes="
\Filament\Support\prepare_inherited_attributes(
new \Illuminate\View\ComponentAttributeBag([
'wire:loading.delay.' . config('filament.livewire_loading_delay', 'default') => '',
'wire:target' => $loadingIndicatorTarget,
])
)->class([$iconClasses])
"
/>
@endif
@if (filled($badge))
<div class="{{ $badgeContainerClasses }}">
<x-filament::badge :color="$badgeColor" :size="$badgeSize">
{{ $badge }}
</x-filament::badge>
</div>
@endif
</button>
@elseif ($tag === 'a')
<a
{{ \Filament\Support\generate_href_html($href, $target === '_blank', $spaMode) }}
@if ($keyBindings || $hasTooltip)
x-data="{}"
@endif
@if ($keyBindings)
x-bind:id="$id('key-bindings')"
x-mousetrap.global.{{ collect($keyBindings)->map(fn (string $keyBinding): string => str_replace('+', '-', $keyBinding))->implode('.') }}="document.getElementById($el.id).click()"
@endif
@if ($hasTooltip)
x-tooltip="{
content: @js($tooltip),
theme: $store.theme,
}"
@endif
{{
$attributes
->merge([
'title' => $hasTooltip ? null : $label,
], escape: true)
->class([$buttonClasses])
->style([$buttonStyles])
}}
>
@if ($label)
<span class="sr-only">
{{ $label }}
</span>
@endif
<x-filament::icon
:alias="$iconAlias"
:icon="$icon"
:class="$iconClasses"
/>
@if (filled($badge))
<div class="{{ $badgeContainerClasses }}">
<x-filament::badge :color="$badgeColor" size="xs">
{{ $badge }}
</x-filament::badge>
</div>
@endif
</a>
@endif

View File

@@ -0,0 +1,29 @@
@props([
'alias' => null,
'class' => '',
'icon' => null,
])
@php
$icon = ($alias ? \Filament\Support\Facades\FilamentIcon::resolve($alias) : null) ?: ($icon ?? $slot);
@endphp
@if ($icon instanceof \Illuminate\Contracts\Support\Htmlable)
<span {{ $attributes->class($class) }}>
{{ $icon }}
</span>
@elseif (str_contains($icon, '/'))
<img
{{
$attributes
->merge(['src' => $icon])
->class($class)
}}
/>
@else
@svg(
$icon,
$class,
array_filter($attributes->getAttributes()),
)
@endif

View File

@@ -0,0 +1,29 @@
@props([
'alpineValid' => null,
'valid' => true,
])
@php
$hasAlpineValidClasses = filled($alpineValid);
$validInputClasses = 'text-primary-600 ring-gray-950/10 focus:ring-primary-600 checked:focus:ring-primary-500/50 dark:text-primary-500 dark:ring-white/20 dark:checked:bg-primary-500 dark:focus:ring-primary-500 dark:checked:focus:ring-primary-400/50 dark:disabled:ring-white/10';
$invalidInputClasses = 'fi-invalid text-danger-600 ring-danger-600 focus:ring-danger-600 checked:focus:ring-danger-500/50 dark:text-danger-500 dark:ring-danger-500 dark:checked:bg-danger-500 dark:focus:ring-danger-500 dark:checked:focus:ring-danger-400/50';
@endphp
<input
type="checkbox"
@if ($hasAlpineValidClasses)
x-bind:class="{
@js($validInputClasses): {{ $alpineValid }},
@js($invalidInputClasses): {{ "(! {$alpineValid})" }},
}"
@endif
{{
$attributes
->class([
'fi-checkbox-input rounded border-none bg-white shadow-sm ring-1 transition duration-75 checked:ring-0 focus:ring-2 focus:ring-offset-0 disabled:pointer-events-none disabled:bg-gray-50 disabled:text-gray-50 disabled:checked:bg-gray-400 disabled:checked:text-gray-400 dark:bg-white/5 dark:disabled:bg-transparent dark:disabled:checked:bg-gray-600',
$validInputClasses => (! $hasAlpineValidClasses) && $valid,
$invalidInputClasses => (! $hasAlpineValidClasses) && (! $valid),
])
}}
/>

View File

@@ -0,0 +1,22 @@
@props([
'inlinePrefix' => false,
'inlineSuffix' => false,
])
<input
{{
$attributes->class([
'fi-input block w-full border-none py-1.5 text-base text-gray-950 transition duration-75 placeholder:text-gray-400 focus:ring-0 disabled:text-gray-500 disabled:[-webkit-text-fill-color:theme(colors.gray.500)] disabled:placeholder:[-webkit-text-fill-color:theme(colors.gray.400)] dark:text-white dark:placeholder:text-gray-500 dark:disabled:text-gray-400 dark:disabled:[-webkit-text-fill-color:theme(colors.gray.400)] dark:disabled:placeholder:[-webkit-text-fill-color:theme(colors.gray.500)] sm:text-sm sm:leading-6',
// A fully transparent white background color is used
// instead of transparent to fix a Safari bug
// where the date/time input "placeholder" colors too dark.
//
// https://github.com/filamentphp/filament/issues/7087
'bg-white/0',
'ps-0' => $inlinePrefix,
'ps-3' => ! $inlinePrefix,
'pe-0' => $inlineSuffix,
'pe-3' => ! $inlineSuffix,
])
}}
/>

View File

@@ -0,0 +1,15 @@
@props([
'valid' => true,
])
<input
type="radio"
{{
$attributes
->class([
'fi-radio-input border-none bg-white shadow-sm ring-1 transition duration-75 checked:ring-0 focus:ring-2 focus:ring-offset-0 disabled:bg-gray-50 disabled:text-gray-50 disabled:checked:bg-gray-400 disabled:checked:text-gray-400 dark:bg-white/5 dark:disabled:bg-transparent dark:disabled:checked:bg-gray-600',
'text-primary-600 ring-gray-950/10 focus:ring-primary-600 checked:focus:ring-primary-500/50 dark:text-primary-500 dark:ring-white/20 dark:checked:bg-primary-500 dark:focus:ring-primary-500 dark:checked:focus:ring-primary-400/50 dark:disabled:ring-white/10' => $valid,
'fi-invalid text-danger-600 ring-danger-600 focus:ring-danger-600 checked:focus:ring-danger-500/50 dark:text-danger-500 dark:ring-danger-500 dark:checked:bg-danger-500 dark:focus:ring-danger-500 dark:checked:focus:ring-danger-400/50' => ! $valid,
])
}}
/>

View File

@@ -0,0 +1,16 @@
@props([
'inlinePrefix' => false,
'inlineSuffix' => false,
])
<select
{{
$attributes->class([
'fi-select-input block w-full border-none bg-transparent py-1.5 pe-8 text-base text-gray-950 transition duration-75 focus:ring-0 disabled:text-gray-500 disabled:[-webkit-text-fill-color:theme(colors.gray.500)] dark:text-white dark:disabled:text-gray-400 dark:disabled:[-webkit-text-fill-color:theme(colors.gray.400)] sm:text-sm sm:leading-6 [&_optgroup]:bg-white [&_optgroup]:dark:bg-gray-900 [&_option]:bg-white [&_option]:dark:bg-gray-900',
'ps-0' => $inlinePrefix,
'ps-3' => ! $inlinePrefix,
])
}}
>
{{ $slot }}
</select>

View File

@@ -0,0 +1,212 @@
@props([
'alpineDisabled' => null,
'alpineValid' => null,
'disabled' => false,
'inlinePrefix' => false,
'inlineSuffix' => false,
'prefix' => null,
'prefixActions' => [],
'prefixIcon' => null,
'prefixIconColor' => 'gray',
'prefixIconAlias' => null,
'suffix' => null,
'suffixActions' => [],
'suffixIcon' => null,
'suffixIconColor' => 'gray',
'suffixIconAlias' => null,
'valid' => true,
])
@php
$prefixActions = array_filter(
$prefixActions,
fn (\Filament\Forms\Components\Actions\Action $prefixAction): bool => $prefixAction->isVisible(),
);
$suffixActions = array_filter(
$suffixActions,
fn (\Filament\Forms\Components\Actions\Action $suffixAction): bool => $suffixAction->isVisible(),
);
$hasPrefix = count($prefixActions) || $prefixIcon || filled($prefix);
$hasSuffix = count($suffixActions) || $suffixIcon || filled($suffix);
$hasAlpineDisabledClasses = filled($alpineDisabled);
$hasAlpineValidClasses = filled($alpineValid);
$hasAlpineClasses = $hasAlpineDisabledClasses || $hasAlpineValidClasses;
$enabledWrapperClasses = 'bg-white dark:bg-white/5 [&:not(:has(.fi-ac-action:focus))]:focus-within:ring-2';
$disabledWrapperClasses = 'fi-disabled bg-gray-50 dark:bg-transparent';
$validWrapperClasses = 'ring-gray-950/10';
$invalidWrapperClasses = 'fi-invalid ring-danger-600 dark:ring-danger-500';
$enabledValidWrapperClasses = 'dark:ring-white/20 [&:not(:has(.fi-ac-action:focus))]:focus-within:ring-primary-600 dark:[&:not(:has(.fi-ac-action:focus))]:focus-within:ring-primary-500';
$enabledInvalidWrapperClasses = '[&:not(:has(.fi-ac-action:focus))]:focus-within:ring-danger-600 dark:[&:not(:has(.fi-ac-action:focus))]:focus-within:ring-danger-500';
$disabledValidWrapperClasses = 'dark:ring-white/10';
$actionsClasses = 'flex items-center gap-3';
$labelClasses = 'fi-input-wrp-label whitespace-nowrap text-sm text-gray-500 dark:text-gray-400';
$getIconClasses = fn (string | array $color = 'gray'): string => \Illuminate\Support\Arr::toCssClasses([
'fi-input-wrp-icon h-5 w-5',
match ($color) {
'gray' => 'text-gray-400 dark:text-gray-500',
default => 'text-custom-500',
},
]);
$getIconStyles = fn (string | array $color = 'gray'): string => \Illuminate\Support\Arr::toCssStyles([
\Filament\Support\get_color_css_variables(
$color,
shades: [500],
alias: 'input-wrapper.icon',
) => $color !== 'gray',
]);
$wireTarget = $attributes->whereStartsWith(['wire:target'])->first();
$hasLoadingIndicator = filled($wireTarget);
if ($hasLoadingIndicator) {
$loadingIndicatorTarget = html_entity_decode($wireTarget, ENT_QUOTES);
}
@endphp
<div
@if ($hasAlpineClasses)
x-bind:class="{
{{ $hasAlpineDisabledClasses ? "'{$enabledWrapperClasses}': ! ({$alpineDisabled})," : null }}
{{ $hasAlpineDisabledClasses ? "'{$disabledWrapperClasses}': {$alpineDisabled}," : null }}
{{ $hasAlpineValidClasses ? "'{$validWrapperClasses}': {$alpineValid}," : null }}
{{ $hasAlpineValidClasses ? "'{$invalidWrapperClasses}': ! ({$alpineValid})," : null }}
{{ ($hasAlpineDisabledClasses && $hasAlpineValidClasses) ? "'{$enabledValidWrapperClasses}': ! ({$alpineDisabled}) && {$alpineValid}," : null }}
{{ ($hasAlpineDisabledClasses && $hasAlpineValidClasses) ? "'{$enabledInvalidWrapperClasses}': ! ({$alpineDisabled}) && ! ({$alpineValid})," : null }}
{{ ($hasAlpineDisabledClasses && $hasAlpineValidClasses) ? "'{$disabledValidWrapperClasses}': {$alpineDisabled} && ! ({$alpineValid})," : null }}
}"
@endif
{{
$attributes
->except(['wire:target', 'tabindex'])
->class([
'fi-input-wrp flex rounded-lg shadow-sm ring-1 transition duration-75',
$enabledWrapperClasses => (! $hasAlpineClasses) && (! $disabled),
$disabledWrapperClasses => (! $hasAlpineClasses) && $disabled,
$validWrapperClasses => (! $hasAlpineClasses) && $valid,
$invalidWrapperClasses => (! $hasAlpineClasses) && (! $valid),
$enabledValidWrapperClasses => (! $hasAlpineClasses) && (! $disabled) && $valid,
$enabledInvalidWrapperClasses => (! $hasAlpineClasses) && (! $disabled) && (! $valid),
$disabledValidWrapperClasses => (! $hasAlpineClasses) && $disabled && $valid,
])
}}
>
@if ($hasPrefix || $hasLoadingIndicator)
<div
@if (! $hasPrefix)
wire:loading.delay.{{ config('filament.livewire_loading_delay', 'default') }}.flex
wire:target="{{ $loadingIndicatorTarget }}"
wire:key="{{ \Illuminate\Support\Str::random() }}" {{-- Makes sure the loading indicator gets hidden again. --}}
@endif
@class([
'fi-input-wrp-prefix items-center gap-x-3 ps-3',
'flex' => $hasPrefix,
'hidden' => ! $hasPrefix,
'pe-1' => $inlinePrefix && filled($prefix),
'pe-2' => $inlinePrefix && blank($prefix),
'border-e border-gray-200 pe-3 ps-3 dark:border-white/10' => ! $inlinePrefix,
])
>
@if (count($prefixActions))
<div class="{{ $actionsClasses }}">
@foreach ($prefixActions as $prefixAction)
{{ $prefixAction }}
@endforeach
</div>
@endif
@if ($prefixIcon)
<x-filament::icon
:attributes="
\Filament\Support\prepare_inherited_attributes(
new \Illuminate\View\ComponentAttributeBag([
'alias' => $prefixIconAlias,
'icon' => $prefixIcon,
'wire:loading.remove.delay.' . config('filament.livewire_loading_delay', 'default') => $hasLoadingIndicator,
'wire:target' => $hasLoadingIndicator ? $loadingIndicatorTarget : null,
])
)
->class([$getIconClasses($prefixIconColor)])
->style([$getIconStyles($prefixIconColor)])
"
/>
@endif
@if ($hasLoadingIndicator)
<x-filament::loading-indicator
:attributes="
\Filament\Support\prepare_inherited_attributes(
new \Illuminate\View\ComponentAttributeBag([
'wire:loading.delay.' . config('filament.livewire_loading_delay', 'default') => $hasPrefix,
'wire:target' => $hasPrefix ? $loadingIndicatorTarget : null,
])
)->class([$getIconClasses()])
"
/>
@endif
@if (filled($prefix))
<span class="{{ $labelClasses }}">
{{ $prefix }}
</span>
@endif
</div>
@endif
<div
@if ($hasLoadingIndicator && (! $hasPrefix))
@if ($inlinePrefix)
wire:loading.delay.{{ config('filament.livewire_loading_delay', 'default') }}.class.remove="ps-3"
@endif
wire:target="{{ $loadingIndicatorTarget }}"
@endif
@class([
'fi-input-wrp-input min-w-0 flex-1',
'ps-3' => $hasLoadingIndicator && (! $hasPrefix) && $inlinePrefix,
])
>
{{ $slot }}
</div>
@if ($hasSuffix)
<div
@class([
'fi-input-wrp-suffix flex items-center gap-x-3 pe-3',
'ps-1' => $inlineSuffix && filled($suffix),
'ps-2' => $inlineSuffix && blank($suffix),
'border-s border-gray-200 ps-3 dark:border-white/10' => ! $inlineSuffix,
])
>
@if (filled($suffix))
<span class="{{ $labelClasses }}">
{{ $suffix }}
</span>
@endif
@if ($suffixIcon)
<x-filament::icon
:alias="$suffixIconAlias"
:icon="$suffixIcon"
:class="$getIconClasses($suffixIconColor)"
:style="$getIconStyles($suffixIconColor)"
/>
@endif
@if (count($suffixActions))
<div class="{{ $actionsClasses }}">
@foreach ($suffixActions as $suffixAction)
{{ $suffixAction }}
@endforeach
</div>
@endif
</div>
@endif
</div>

View File

@@ -0,0 +1,304 @@
@php
use Filament\Support\Enums\ActionSize;
use Filament\Support\Enums\FontWeight;
use Filament\Support\Enums\IconPosition;
use Filament\Support\Enums\IconSize;
@endphp
@props([
'badge' => null,
'badgeColor' => 'primary',
'badgeSize' => 'xs',
'color' => 'primary',
'disabled' => false,
'form' => null,
'formId' => null,
'href' => null,
'icon' => null,
'iconAlias' => null,
'iconPosition' => IconPosition::Before,
'iconSize' => null,
'keyBindings' => null,
'labelSrOnly' => false,
'loadingIndicator' => true,
'size' => ActionSize::Medium,
'spaMode' => null,
'tag' => 'a',
'target' => null,
'tooltip' => null,
'type' => 'button',
'weight' => FontWeight::SemiBold,
])
@php
if (! $iconPosition instanceof IconPosition) {
$iconPosition = filled($iconPosition) ? (IconPosition::tryFrom($iconPosition) ?? $iconPosition) : null;
}
if (! $size instanceof ActionSize) {
$size = filled($size) ? (ActionSize::tryFrom($size) ?? $size) : null;
}
$iconSize ??= match ($size) {
ActionSize::ExtraSmall, ActionSize::Small => IconSize::Small,
default => IconSize::Medium,
};
if (! $iconSize instanceof IconSize) {
$iconSize = filled($iconSize) ? (IconSize::tryFrom($iconSize) ?? $iconSize) : null;
}
$linkClasses = \Illuminate\Support\Arr::toCssClasses([
'fi-link group/link relative inline-flex items-center justify-center outline-none',
'pointer-events-none opacity-70' => $disabled,
($size instanceof ActionSize) ? "fi-size-{$size->value}" : null,
// @deprecated `fi-link-size-*` has been replaced by `fi-size-*`.
($size instanceof ActionSize) ? "fi-link-size-{$size->value}" : null,
match ($size) {
ActionSize::ExtraSmall => 'gap-1',
ActionSize::Small => 'gap-1',
ActionSize::Medium => 'gap-1.5',
ActionSize::Large => 'gap-1.5',
ActionSize::ExtraLarge => 'gap-1.5',
default => $size,
},
match ($color) {
'gray' => null,
default => 'fi-color-custom',
},
is_string($color) ? "fi-color-{$color}" : null,
]);
if (! $labelSrOnly) {
$labelClasses = \Illuminate\Support\Arr::toCssClasses([
match ($weight) {
FontWeight::Thin, 'thin' => 'font-thin',
FontWeight::ExtraLight, 'extralight' => 'font-extralight',
FontWeight::Light, 'light' => 'font-light',
FontWeight::Medium, 'medium' => 'font-medium',
FontWeight::Normal, 'normal' => 'font-normal',
FontWeight::SemiBold, 'semibold' => 'font-semibold',
FontWeight::Bold, 'bold' => 'font-bold',
FontWeight::ExtraBold, 'extrabold' => 'font-extrabold',
FontWeight::Black, 'black' => 'font-black',
default => $weight,
},
match ($size) {
ActionSize::ExtraSmall => 'text-xs',
ActionSize::Small => 'text-sm',
ActionSize::Medium => 'text-sm',
ActionSize::Large => 'text-sm',
ActionSize::ExtraLarge => 'text-sm',
default => null,
},
match ($color) {
'gray' => 'text-gray-700 dark:text-gray-200',
default => 'text-custom-600 dark:text-custom-400',
},
'group-hover/link:underline group-focus-visible/link:underline',
]);
} else {
$labelClasses = 'sr-only';
}
$labelStyles = \Illuminate\Support\Arr::toCssStyles([
\Filament\Support\get_color_css_variables(
$color,
shades: [400, 600],
alias: 'link.label',
) => $color !== 'gray',
]);
$iconClasses = \Illuminate\Support\Arr::toCssClasses([
'fi-link-icon',
match ($iconSize) {
IconSize::Small => 'h-4 w-4',
IconSize::Medium => 'h-5 w-5',
IconSize::Large => 'h-6 w-6',
default => $iconSize,
},
match ($color) {
'gray' => 'text-gray-400 dark:text-gray-500',
default => 'text-custom-600 dark:text-custom-400',
},
]);
$iconStyles = \Illuminate\Support\Arr::toCssStyles([
\Filament\Support\get_color_css_variables(
$color,
shades: [400, 600],
alias: 'link.icon',
) => $color !== 'gray',
]);
$badgeContainerClasses = 'fi-link-badge-ctn absolute start-full top-0 z-[1] w-max -translate-x-1/4 -translate-y-3/4 rounded-md bg-white dark:bg-gray-900 rtl:translate-x-1/4';
$wireTarget = $loadingIndicator ? $attributes->whereStartsWith(['wire:target', 'wire:click'])->filter(fn ($value): bool => filled($value))->first() : null;
$hasLoadingIndicator = filled($wireTarget) || ($type === 'submit' && filled($form));
if ($hasLoadingIndicator) {
$loadingIndicatorTarget = html_entity_decode($wireTarget ?: $form, ENT_QUOTES);
}
$hasTooltip = filled($tooltip);
@endphp
@if ($tag === 'a')
<a
{{ \Filament\Support\generate_href_html($href, $target === '_blank', $spaMode) }}
@if ($keyBindings || $hasTooltip)
x-data="{}"
@endif
@if ($keyBindings)
x-bind:id="$id('key-bindings')"
x-mousetrap.global.{{ collect($keyBindings)->map(fn (string $keyBinding): string => str_replace('+', '-', $keyBinding))->implode('.') }}="document.getElementById($el.id).click()"
@endif
@if ($hasTooltip)
x-tooltip="{
content: @js($tooltip),
theme: $store.theme,
}"
@endif
{{ $attributes->class([$linkClasses]) }}
>
@if ($icon && $iconPosition === IconPosition::Before)
<x-filament::icon
:alias="$iconAlias"
:icon="$icon"
:class="$iconClasses"
:style="$iconStyles"
/>
@endif
<span class="{{ $labelClasses }}" style="{{ $labelStyles }}">
{{ $slot }}
</span>
@if ($icon && $iconPosition === IconPosition::After)
<x-filament::icon
:alias="$iconAlias"
:icon="$icon"
:class="$iconClasses"
:style="$iconStyles"
/>
@endif
@if (filled($badge))
<div class="{{ $badgeContainerClasses }}">
<x-filament::badge :color="$badgeColor" :size="$badgeSize">
{{ $badge }}
</x-filament::badge>
</div>
@endif
</a>
@trim
@elseif ($tag === 'button')
<button
@if ($keyBindings || $hasTooltip)
x-data="{}"
@endif
@if ($keyBindings)
x-bind:id="$id('key-bindings')"
x-mousetrap.global.{{ collect($keyBindings)->map(fn (string $keyBinding): string => str_replace('+', '-', $keyBinding))->implode('.') }}="document.getElementById($el.id).click()"
@endif
@if ($hasTooltip)
x-tooltip="{
content: @js($tooltip),
theme: $store.theme,
}"
@endif
{{
$attributes
->merge([
'disabled' => $disabled,
'form' => $formId,
'type' => $type,
'wire:loading.attr' => 'disabled',
'wire:target' => ($hasLoadingIndicator && $loadingIndicatorTarget) ? $loadingIndicatorTarget : null,
], escape: false)
->class([$linkClasses])
}}
>
@if ($iconPosition === IconPosition::Before)
@if ($icon)
<x-filament::icon
:attributes="
\Filament\Support\prepare_inherited_attributes(
new \Illuminate\View\ComponentAttributeBag([
'alias' => $iconAlias,
'icon' => $icon,
'wire:loading.remove.delay.' . config('filament.livewire_loading_delay', 'default') => $hasLoadingIndicator,
'wire:target' => $hasLoadingIndicator ? $loadingIndicatorTarget : null,
])
)
->class([$iconClasses])
->style([$iconStyles])
"
/>
@endif
@if ($hasLoadingIndicator)
<x-filament::loading-indicator
:attributes="
\Filament\Support\prepare_inherited_attributes(
new \Illuminate\View\ComponentAttributeBag([
'wire:loading.delay.' . config('filament.livewire_loading_delay', 'default') => '',
'wire:target' => $loadingIndicatorTarget,
])
)
->class([$iconClasses])
->style([$iconStyles])
"
/>
@endif
@endif
<span class="{{ $labelClasses }}" style="{{ $labelStyles }}">
{{ $slot }}
</span>
@if ($iconPosition === IconPosition::After)
@if ($icon)
<x-filament::icon
:attributes="
\Filament\Support\prepare_inherited_attributes(
new \Illuminate\View\ComponentAttributeBag([
'alias' => $iconAlias,
'icon' => $icon,
'wire:loading.remove.delay.' . config('filament.livewire_loading_delay', 'default') => $hasLoadingIndicator,
'wire:target' => $hasLoadingIndicator ? $loadingIndicatorTarget : null,
])
)
->class([$iconClasses])
->style([$iconStyles])
"
/>
@endif
@if ($hasLoadingIndicator)
<x-filament::loading-indicator
:attributes="
\Filament\Support\prepare_inherited_attributes(
new \Illuminate\View\ComponentAttributeBag([
'wire:loading.delay.' . config('filament.livewire_loading_delay', 'default') => '',
'wire:target' => $loadingIndicatorTarget,
])
)
->class([$iconClasses])
->style([$iconStyles])
"
/>
@endif
@endif
@if (filled($badge))
<div class="{{ $badgeContainerClasses }}">
<x-filament::badge :color="$badgeColor" :size="$badgeSize">
{{ $badge }}
</x-filament::badge>
</div>
@endif
</button>
@trim
@endif

View File

@@ -0,0 +1,18 @@
<svg
fill="none"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
{{ $attributes->class(['animate-spin']) }}
>
<path
clip-rule="evenodd"
d="M12 19C15.866 19 19 15.866 19 12C19 8.13401 15.866 5 12 5C8.13401 5 5 8.13401 5 12C5 15.866 8.13401 19 12 19ZM12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22Z"
fill-rule="evenodd"
fill="currentColor"
opacity="0.2"
></path>
<path
d="M2 12C2 6.47715 6.47715 2 12 2V5C8.13401 5 5 8.13401 5 12H2Z"
fill="currentColor"
></path>
</svg>

After

Width:  |  Height:  |  Size: 628 B

View File

@@ -0,0 +1,33 @@
@php
if ((! isset($columnSpan)) || (! is_array($columnSpan))) {
$columnSpan = [
'default' => $columnSpan ?? null,
];
}
if ((! isset($columnStart)) || (! is_array($columnStart))) {
$columnStart = [
'default' => $columnStart ?? null,
];
}
$height ??= '8rem';
@endphp
<x-filament::grid.column
:default="$columnSpan['default'] ?? 1"
:sm="$columnSpan['sm'] ?? null"
:md="$columnSpan['md'] ?? null"
:lg="$columnSpan['lg'] ?? null"
:xl="$columnSpan['xl'] ?? null"
:twoXl="$columnSpan['2xl'] ?? null"
:defaultStart="$columnStart['default'] ?? null"
:smStart="$columnStart['sm'] ?? null"
:mdStart="$columnStart['md'] ?? null"
:lgStart="$columnStart['lg'] ?? null"
:xlStart="$columnStart['xl'] ?? null"
:twoXlStart="$columnStart['2xl'] ?? null"
class="fi-loading-section"
>
<x-filament::section class="animate-pulse" style="height: {{ $height }}" />
</x-filament::grid.column>

View File

@@ -0,0 +1,5 @@
<p
{{ $attributes->class(['fi-modal-description text-sm text-gray-500 dark:text-gray-400']) }}
>
{{ $slot }}
</p>

View File

@@ -0,0 +1,5 @@
<h2
{{ $attributes->class(['fi-modal-heading text-base font-semibold leading-6 text-gray-950 dark:text-white']) }}
>
{{ $slot }}
</h2>

Some files were not shown because too many files have changed in this diff Show More