410 lines
12 KiB
PHP
410 lines
12 KiB
PHP
@extends('layouts.winemaker')
|
|
|
|
@section('title', 'Edit Vineyard Map')
|
|
|
|
@push('styles')
|
|
<style>
|
|
.map-edit-layout {
|
|
display: grid;
|
|
grid-template-columns: 260px 1fr;
|
|
gap: 2rem;
|
|
min-height: 70vh;
|
|
}
|
|
|
|
.editor-sidebar {
|
|
background: #ffffff;
|
|
border-radius: 12px;
|
|
padding: 1.5rem;
|
|
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08);
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 1.5rem;
|
|
}
|
|
|
|
.edit-panel {
|
|
background: #ffffff;
|
|
border-radius: 12px;
|
|
padding: 1.5rem;
|
|
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08);
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 1rem;
|
|
}
|
|
|
|
.map-canvas-wrapper {
|
|
position: relative;
|
|
border: 1px dashed #cbd5e1;
|
|
border-radius: 12px;
|
|
min-height: 520px;
|
|
overflow: auto;
|
|
background: repeating-linear-gradient(90deg, rgba(99, 102, 241, 0.06) 0, rgba(99, 102, 241, 0.06) 1px, transparent 1px, transparent 96px),
|
|
repeating-linear-gradient(180deg, rgba(99, 102, 241, 0.06) 0, rgba(99, 102, 241, 0.06) 1px, transparent 1px, transparent 96px);
|
|
}
|
|
|
|
.vineyard-map-grid {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
transform-origin: top left;
|
|
}
|
|
|
|
.map-cell {
|
|
position: absolute;
|
|
border-radius: 12px;
|
|
border: 1px solid rgba(79, 70, 229, 0.35);
|
|
background: rgba(99, 102, 241, 0.18);
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 0.35rem;
|
|
color: #1a365d;
|
|
box-shadow: 0 10px 20px rgba(15, 23, 42, 0.08);
|
|
transition: transform 0.15s ease, box-shadow 0.15s ease, background 0.2s ease, border-color 0.2s ease;
|
|
cursor: pointer;
|
|
user-select: none;
|
|
}
|
|
|
|
.map-cell::after {
|
|
content: '';
|
|
position: absolute;
|
|
inset: 6px;
|
|
border-radius: 8px;
|
|
border: 1px dashed rgba(79, 70, 229, 0.2);
|
|
pointer-events: none;
|
|
}
|
|
|
|
.map-cell-count {
|
|
font-size: 1.05rem;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.map-cell-variety {
|
|
font-size: 0.75rem;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.04em;
|
|
opacity: 0.8;
|
|
}
|
|
|
|
.map-cell.selected {
|
|
background: rgba(59, 130, 246, 0.35);
|
|
border-color: rgba(37, 99, 235, 0.9);
|
|
box-shadow: 0 14px 26px rgba(37, 99, 235, 0.35);
|
|
}
|
|
|
|
.map-cell.remove-selected {
|
|
outline: 3px solid rgba(239, 68, 68, 0.65);
|
|
outline-offset: 2px;
|
|
}
|
|
|
|
.map-cell.status-inactive {
|
|
background: rgba(148, 163, 184, 0.18);
|
|
border-color: rgba(148, 163, 184, 0.7);
|
|
}
|
|
|
|
.map-cell.status-discarded {
|
|
background: rgba(239, 68, 68, 0.25);
|
|
border-color: rgba(239, 68, 68, 0.6);
|
|
}
|
|
|
|
.map-cell.status-harvested {
|
|
background: rgba(16, 185, 129, 0.22);
|
|
border-color: rgba(5, 150, 105, 0.6);
|
|
}
|
|
|
|
.map-secondary-nav {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 0.5rem;
|
|
align-items: center;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.map-secondary-nav a {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.35rem;
|
|
padding: 0.45rem 0.85rem;
|
|
border-radius: 999px;
|
|
background: rgba(148, 163, 184, 0.2);
|
|
color: #1e293b;
|
|
text-decoration: none;
|
|
font-size: 0.8rem;
|
|
font-weight: 600;
|
|
transition: background 0.2s ease, color 0.2s ease;
|
|
}
|
|
|
|
.map-secondary-nav a:hover {
|
|
background: rgba(37, 99, 235, 0.18);
|
|
color: #1d4ed8;
|
|
}
|
|
|
|
.map-secondary-nav a.active {
|
|
background: rgba(37, 99, 235, 0.28);
|
|
color: #1d4ed8;
|
|
}
|
|
|
|
.pending-cell {
|
|
position: absolute;
|
|
border: 2px dashed rgba(37, 99, 235, 0.85);
|
|
border-radius: 12px;
|
|
background: rgba(59, 130, 246, 0.18);
|
|
pointer-events: none;
|
|
box-shadow: inset 0 0 0 2px rgba(59, 130, 246, 0.25);
|
|
display: none;
|
|
transition: opacity 0.15s ease;
|
|
opacity: 0.85;
|
|
}
|
|
|
|
.mode-switch {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.75rem;
|
|
}
|
|
|
|
.mode-switch button {
|
|
border: 1px solid #dce5f3;
|
|
border-radius: 10px;
|
|
padding: 0.85rem 1rem;
|
|
background: #f4f7fb;
|
|
color: #1a365d;
|
|
font-weight: 600;
|
|
text-align: left;
|
|
transition: all 0.2s ease;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.mode-switch button:hover,
|
|
.mode-switch button.active {
|
|
background: linear-gradient(135deg, #7fb4e0 0%, #9dcaeb 100%);
|
|
color: #ffffff;
|
|
border-color: transparent;
|
|
}
|
|
|
|
.mode-panels {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 1.25rem;
|
|
}
|
|
|
|
.mode-panel {
|
|
display: none;
|
|
flex-direction: column;
|
|
gap: 0.85rem;
|
|
}
|
|
|
|
.mode-panel.active {
|
|
display: flex;
|
|
}
|
|
|
|
.mode-panel form,
|
|
.mode-panel .panel-body {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.85rem;
|
|
}
|
|
|
|
.editor-sidebar label {
|
|
font-weight: 600;
|
|
color: #1a365d;
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
.editor-sidebar input,
|
|
.editor-sidebar select,
|
|
.editor-sidebar textarea {
|
|
width: 100%;
|
|
border-radius: 8px;
|
|
border: 1px solid #cbd5e1;
|
|
padding: 0.6rem 0.75rem;
|
|
font-size: 0.95rem;
|
|
}
|
|
|
|
.editor-sidebar button {
|
|
border: none;
|
|
border-radius: 8px;
|
|
padding: 0.7rem 1rem;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.btn-primary {
|
|
background: #2563eb;
|
|
color: #ffffff;
|
|
}
|
|
|
|
.btn-danger {
|
|
background: #ef4444;
|
|
color: #ffffff;
|
|
}
|
|
|
|
.btn-secondary {
|
|
background: #1e293b;
|
|
color: #ffffff;
|
|
}
|
|
|
|
.info-card {
|
|
margin-bottom: 1.5rem;
|
|
background: #f1f5f9;
|
|
border-radius: 10px;
|
|
padding: 1rem;
|
|
color: #475569;
|
|
}
|
|
|
|
.map-legend {
|
|
display: flex;
|
|
gap: 1rem;
|
|
font-size: 0.85rem;
|
|
color: #475569;
|
|
}
|
|
|
|
.map-feedback {
|
|
margin-top: 0.75rem;
|
|
font-size: 0.9rem;
|
|
color: #475569;
|
|
}
|
|
|
|
.map-feedback.feedback-success {
|
|
color: #0f766e;
|
|
}
|
|
|
|
.map-feedback.feedback-error {
|
|
color: #dc2626;
|
|
}
|
|
|
|
.map-feedback.feedback-info {
|
|
color: #475569;
|
|
}
|
|
|
|
.mode-actions {
|
|
display: flex;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.mode-panel-divider {
|
|
height: 1px;
|
|
background: #e2e8f0;
|
|
}
|
|
|
|
.map-selection-layer {
|
|
position: absolute;
|
|
inset: 0;
|
|
pointer-events: none;
|
|
user-select: none;
|
|
}
|
|
|
|
.selection-box {
|
|
position: absolute;
|
|
border: 1px solid rgba(37, 99, 235, 0.85);
|
|
background: rgba(59, 130, 246, 0.2);
|
|
border-radius: 10px;
|
|
box-shadow: 0 0 0 1px rgba(37, 99, 235, 0.35);
|
|
pointer-events: none;
|
|
}
|
|
|
|
.add-marker {
|
|
position: absolute;
|
|
border: 2px dashed rgba(16, 185, 129, 0.8);
|
|
border-radius: 12px;
|
|
background: rgba(110, 231, 183, 0.2);
|
|
box-shadow: inset 0 0 0 2px rgba(16, 185, 129, 0.2);
|
|
pointer-events: none;
|
|
}
|
|
|
|
.selection-summary {
|
|
font-size: 0.85rem;
|
|
color: #64748b;
|
|
line-height: 1.4;
|
|
}
|
|
</style>
|
|
@endpush
|
|
|
|
@section('content')
|
|
<div class="map-edit-layout" data-page="vineyard-map-edit">
|
|
<aside class="editor-sidebar">
|
|
<div>
|
|
<h2 style="font-size:1.4rem; color:#1a365d; margin:0;">Map actions</h2>
|
|
<p style="font-size:0.9rem; color:#475569; margin:0.5rem 0 0;">Choose what you want to do, then use the map on the right.</p>
|
|
</div>
|
|
|
|
<div class="mode-switch" data-role="mode-switch">
|
|
<button type="button" data-mode="edit" class="active">Edit rows</button>
|
|
<button type="button" data-mode="add">Add rows</button>
|
|
<button type="button" data-mode="remove">Remove rows</button>
|
|
</div>
|
|
|
|
<div class="mode-panels">
|
|
<div class="mode-panel active" data-mode-panel="edit">
|
|
<form data-role="row-form">
|
|
<input type="hidden" name="row_id">
|
|
<label>Current location
|
|
<input type="text" name="location" readonly>
|
|
</label>
|
|
<label>Head count
|
|
<input type="number" name="vine_count" min="0" step="1" required>
|
|
</label>
|
|
<p style="font-size:0.85rem; color:#64748b;">Click an empty cell to stage a new location. Pending: <span data-role="relocation-target">None</span></p>
|
|
<button type="button" class="btn-secondary" data-control="clear-relocation" style="align-self:flex-start;">Clear staged move</button>
|
|
<div style="display:flex; gap:0.5rem;">
|
|
<button type="submit" class="btn-primary" style="flex:1;" disabled>Save row</button>
|
|
<button type="button" class="btn-danger" data-control="delete-row" disabled>Remove</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<div class="mode-panel" data-mode-panel="add">
|
|
<div class="panel-body">
|
|
<h3 style="margin:0; color:#1a365d;">Add rows</h3>
|
|
<p style="font-size:0.9rem; color:#475569;">Click empty squares to stage new rows. Hold Shift or drag to select multiple locations.</p>
|
|
<div class="mode-panel-divider"></div>
|
|
<label>Head count per row
|
|
<input type="number" name="add_vine_count" min="0" step="1" value="0">
|
|
</label>
|
|
<div class="mode-actions">
|
|
<button type="button" class="btn-primary" data-control="commit-add-selection" disabled>Create rows</button>
|
|
<button type="button" class="btn-secondary" data-control="clear-add-selection" disabled>Clear selection</button>
|
|
</div>
|
|
<p class="selection-summary">Selected squares: <span data-role="add-selection-count">0</span></p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mode-panel" data-mode-panel="remove">
|
|
<div class="panel-body">
|
|
<h3 style="margin:0; color:#1a365d;">Remove rows</h3>
|
|
<p style="font-size:0.9rem; color:#475569;">Click rows to mark them for deletion. Hold Shift or drag to select multiple.</p>
|
|
<div class="mode-actions">
|
|
<button type="button" class="btn-danger" data-control="commit-remove-selection" disabled>Delete selected</button>
|
|
<button type="button" class="btn-secondary" data-control="clear-remove-selection" disabled>Clear selection</button>
|
|
</div>
|
|
<p class="selection-summary">Selected rows: <span data-role="remove-selection-count">0</span></p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</aside>
|
|
|
|
<section class="edit-panel">
|
|
<div class="info-card">
|
|
<strong>Map editor:</strong> Choose an action on the left, then work directly on the grid.
|
|
<p class="map-feedback feedback-info" data-role="map-feedback" aria-live="polite">Select a row to edit or click an empty cell to stage a new location.</p>
|
|
</div>
|
|
<div class="map-secondary-nav" aria-label="Related vineyard navigation">
|
|
<a href="{{ route('vineyard.map') }}">Map Overview</a>
|
|
<a href="{{ route('vineyard.map.edit') }}" class="active" aria-current="page">Map Editor</a>
|
|
<a href="{{ route('vineyard-rows.index') }}">Row Directory</a>
|
|
</div>
|
|
<div class="map-canvas-wrapper" data-role="map-container"></div>
|
|
</section>
|
|
</div>
|
|
|
|
@php
|
|
$vineyardEditRoutes = [
|
|
'update' => route('vineyard.updateHeadCount'),
|
|
'add' => route('vineyard.addRows'),
|
|
'destroy' => route('vineyard.rows.destroy', ['vineyardRow' => '__ROW__']),
|
|
'remove' => route('vineyard.rows.destroyMany'),
|
|
];
|
|
@endphp
|
|
|
|
<script type="application/json" id="vineyard-edit-rows">@json($rows)</script>
|
|
<script type="application/json" id="vineyard-edit-routes">@json($vineyardEditRoutes)</script>
|
|
@endsection
|