feat: enhance 'Almoxarifado' UI with improved styling, updated component layouts, and added barcode functionality for better inventory management and user experience
This commit is contained in:
@@ -71,11 +71,7 @@
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
|
||||
function resizeImage(
|
||||
dataUrl: string,
|
||||
maxWidth: number,
|
||||
maxHeight: number
|
||||
): Promise<string> {
|
||||
function resizeImage(dataUrl: string, maxWidth: number, maxHeight: number): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const img = new window.Image();
|
||||
img.onload = () => {
|
||||
@@ -171,10 +167,10 @@
|
||||
|
||||
// Atribuir stream ao vídeo
|
||||
videoElement.srcObject = stream;
|
||||
|
||||
|
||||
// Aguardar o vídeo estar pronto e começar a reproduzir
|
||||
await videoElement.play();
|
||||
|
||||
|
||||
// Aguardar metadata estar carregado
|
||||
if (videoElement.readyState < 2) {
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
@@ -204,10 +200,11 @@
|
||||
} catch (err) {
|
||||
console.error('Erro ao acessar câmera:', err);
|
||||
let errorMessage = 'Erro ao acessar câmera';
|
||||
|
||||
|
||||
if (err instanceof Error) {
|
||||
if (err.name === 'NotAllowedError' || err.name === 'PermissionDeniedError') {
|
||||
errorMessage = 'Permissão de acesso à câmera negada. Por favor, permita o acesso à câmera nas configurações do navegador.';
|
||||
errorMessage =
|
||||
'Permissão de acesso à câmera negada. Por favor, permita o acesso à câmera nas configurações do navegador.';
|
||||
} else if (err.name === 'NotFoundError' || err.name === 'DevicesNotFoundError') {
|
||||
errorMessage = 'Nenhuma câmera encontrada no dispositivo.';
|
||||
} else if (err.name === 'NotReadableError' || err.name === 'TrackStartError') {
|
||||
@@ -216,7 +213,7 @@
|
||||
errorMessage = err.message || errorMessage;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
error = errorMessage;
|
||||
showCamera = false;
|
||||
capturing = false;
|
||||
@@ -280,11 +277,14 @@
|
||||
|
||||
// Sincronizar preview com value sempre que value mudar
|
||||
$effect(() => {
|
||||
// Acessar value para criar dependência reativa
|
||||
const currentValue = value;
|
||||
// Sempre sincronizar quando value mudar
|
||||
preview = value;
|
||||
if (currentValue !== preview) {
|
||||
preview = currentValue;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Limpar stream quando o componente for desmontado
|
||||
$effect(() => {
|
||||
return () => {
|
||||
@@ -305,7 +305,11 @@
|
||||
|
||||
{#if preview}
|
||||
<div class="relative inline-block">
|
||||
<img src={preview} alt="Preview da imagem do produto" class="max-w-full max-h-64 rounded-lg" />
|
||||
<img
|
||||
src={preview}
|
||||
alt="Preview da imagem do produto"
|
||||
class="max-h-64 max-w-full rounded-lg"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-circle btn-error absolute top-2 right-2"
|
||||
@@ -318,7 +322,7 @@
|
||||
{:else}
|
||||
<div class="flex flex-col gap-4">
|
||||
<div
|
||||
class="border-2 border-dashed border-base-300 rounded-lg p-8 text-center cursor-pointer hover:border-primary transition-colors"
|
||||
class="border-base-300 hover:border-primary cursor-pointer rounded-lg border-2 border-dashed p-8 text-center transition-colors"
|
||||
onclick={triggerFileInput}
|
||||
role="button"
|
||||
tabindex="0"
|
||||
@@ -329,18 +333,14 @@
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Upload class="h-12 w-12 mx-auto mb-4 text-base-content/40" />
|
||||
<p class="text-base-content/70 font-medium mb-2">Clique para fazer upload da imagem</p>
|
||||
<p class="text-sm text-base-content/50">
|
||||
<Upload class="text-base-content/40 mx-auto mb-4 h-12 w-12" />
|
||||
<p class="text-base-content/70 mb-2 font-medium">Clique para fazer upload da imagem</p>
|
||||
<p class="text-base-content/50 text-sm">
|
||||
PNG, JPG ou GIF até {maxSizeMB}MB
|
||||
</p>
|
||||
</div>
|
||||
<div class="divider text-sm">ou</div>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-outline btn-primary w-full"
|
||||
onclick={openCamera}
|
||||
>
|
||||
<button type="button" class="btn btn-outline btn-primary w-full" onclick={openCamera}>
|
||||
<Camera class="h-5 w-5" />
|
||||
Capturar da Câmera
|
||||
</button>
|
||||
@@ -354,7 +354,7 @@
|
||||
{/if}
|
||||
|
||||
{#if preview}
|
||||
<div class="flex gap-2 mt-4">
|
||||
<div class="mt-4 flex gap-2">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-outline btn-primary flex-1"
|
||||
@@ -363,11 +363,7 @@
|
||||
<ImageIcon class="h-4 w-4" />
|
||||
Alterar Imagem
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-outline btn-primary flex-1"
|
||||
onclick={openCamera}
|
||||
>
|
||||
<button type="button" class="btn btn-sm btn-outline btn-primary flex-1" onclick={openCamera}>
|
||||
<Camera class="h-4 w-4" />
|
||||
Capturar Foto
|
||||
</button>
|
||||
@@ -377,12 +373,15 @@
|
||||
|
||||
<!-- Modal da Câmera -->
|
||||
{#if showCamera}
|
||||
<div class="fixed inset-0 z-50 flex items-center justify-center bg-black/80" onclick={closeCamera}>
|
||||
<div
|
||||
class="fixed inset-0 z-50 flex items-center justify-center bg-black/80"
|
||||
onclick={closeCamera}
|
||||
>
|
||||
<div
|
||||
class="bg-base-100 rounded-lg shadow-2xl p-6 max-w-2xl w-full mx-4"
|
||||
class="bg-base-100 mx-4 w-full max-w-2xl rounded-lg p-6 shadow-2xl"
|
||||
onclick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<div class="mb-4 flex items-center justify-between">
|
||||
<h3 class="text-xl font-bold">Capturar Foto</h3>
|
||||
<button
|
||||
type="button"
|
||||
@@ -394,19 +393,24 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="relative bg-black rounded-lg overflow-hidden mb-4" style="aspect-ratio: 4/3; min-height: 300px;">
|
||||
<div
|
||||
class="relative mb-4 overflow-hidden rounded-lg bg-black"
|
||||
style="aspect-ratio: 4/3; min-height: 300px;"
|
||||
>
|
||||
{#if showCamera}
|
||||
<video
|
||||
bind:this={videoElement}
|
||||
autoplay
|
||||
playsinline
|
||||
muted
|
||||
class="w-full h-full object-cover"
|
||||
style="transform: scaleX(-1); opacity: {capturing ? '1' : '0'}; transition: opacity 0.3s;"
|
||||
class="h-full w-full object-cover"
|
||||
style="transform: scaleX(-1); opacity: {capturing
|
||||
? '1'
|
||||
: '0'}; transition: opacity 0.3s;"
|
||||
></video>
|
||||
{/if}
|
||||
{#if !capturing}
|
||||
<div class="flex items-center justify-center h-full absolute inset-0 z-10">
|
||||
<div class="absolute inset-0 z-10 flex h-full items-center justify-center">
|
||||
<div class="text-center">
|
||||
<span class="loading loading-spinner loading-lg text-primary mb-2"></span>
|
||||
<p class="text-base-content/70 text-sm">Iniciando câmera...</p>
|
||||
@@ -415,20 +419,9 @@
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2 justify-end">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-ghost"
|
||||
onclick={closeCamera}
|
||||
>
|
||||
Cancelar
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary"
|
||||
onclick={capturePhoto}
|
||||
disabled={!capturing}
|
||||
>
|
||||
<div class="flex justify-end gap-2">
|
||||
<button type="button" class="btn btn-ghost" onclick={closeCamera}> Cancelar </button>
|
||||
<button type="button" class="btn btn-primary" onclick={capturePhoto} disabled={!capturing}>
|
||||
<Camera class="h-5 w-5" />
|
||||
Capturar Foto
|
||||
</button>
|
||||
@@ -442,4 +435,3 @@
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user