stash
This commit is contained in:
parent
7c91661be2
commit
796fef81be
37 changed files with 3404 additions and 108 deletions
376
frontend/src/components/workflow/steps/FotoFirstStep4.vue
Normal file
376
frontend/src/components/workflow/steps/FotoFirstStep4.vue
Normal file
|
|
@ -0,0 +1,376 @@
|
|||
<template>
|
||||
<div class="foto-first-step-4">
|
||||
<div class="step-header mb-4">
|
||||
<h4 class="mb-2">Import Completion</h4>
|
||||
<p class="text-muted">Review and finalize your imported items.</p>
|
||||
</div>
|
||||
|
||||
<!-- Import Summary -->
|
||||
<div class="import-summary mb-4">
|
||||
<div class="row">
|
||||
<div class="col-md-3 mb-3">
|
||||
<div class="card text-center">
|
||||
<div class="card-body">
|
||||
<h3 class="text-primary mb-2">{{ totalItems }}</h3>
|
||||
<p class="card-text text-muted mb-0">Items Imported</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 mb-3">
|
||||
<div class="card text-center">
|
||||
<div class="card-body">
|
||||
<h3 class="text-success mb-2">{{ categorizedItems }}</h3>
|
||||
<p class="card-text text-muted mb-0">With Categories</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 mb-3">
|
||||
<div class="card text-center">
|
||||
<div class="card-body">
|
||||
<h3 class="text-info mb-2">{{ itemsWithLocation }}</h3>
|
||||
<p class="card-text text-muted mb-0">With Locations</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 mb-3">
|
||||
<div class="card text-center">
|
||||
<div class="card-body">
|
||||
<h3 class="text-warning mb-2">${{ totalValue }}</h3>
|
||||
<p class="card-text text-muted mb-0">Total Value</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Category Breakdown -->
|
||||
<div class="category-breakdown mb-4">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h6 class="mb-0">Items by Category</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div v-if="Object.keys(categoryBreakdown).length > 0" class="row">
|
||||
<div v-for="(count, category) in categoryBreakdown" :key="category" class="col-sm-6 col-md-4 col-lg-3 mb-2">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<span class="text-capitalize">{{ category || 'Uncategorized' }}</span>
|
||||
<span class="badge bg-secondary">{{ count }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="text-muted text-center py-3">
|
||||
No items to categorize
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Import Options -->
|
||||
<div class="import-options mb-4">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h6 class="mb-0">Import Options</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-check mb-3">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
id="generateQr"
|
||||
v-model="importOptions.generate_qr_codes"
|
||||
>
|
||||
<label class="form-check-label" for="generateQr">
|
||||
Generate QR codes for items
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check mb-3">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
id="sendNotification"
|
||||
v-model="importOptions.send_notification"
|
||||
>
|
||||
<label class="form-check-label" for="sendNotification">
|
||||
Send completion notification
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-check mb-3">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
id="createReport"
|
||||
v-model="importOptions.create_report"
|
||||
>
|
||||
<label class="form-check-label" for="createReport">
|
||||
Create import report
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check mb-3">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
id="autoBackup"
|
||||
v-model="importOptions.auto_backup"
|
||||
>
|
||||
<label class="form-check-label" for="autoBackup">
|
||||
Auto-backup imported data
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Item List -->
|
||||
<div class="item-list mb-4">
|
||||
<div class="card">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h6 class="mb-0">Imported Items</h6>
|
||||
<div class="btn-group btn-group-sm">
|
||||
<button
|
||||
class="btn"
|
||||
:class="viewMode === 'grid' ? 'btn-primary' : 'btn-outline-primary'"
|
||||
@click="viewMode = 'grid'"
|
||||
>
|
||||
<b-icon-grid></b-icon-grid>
|
||||
</button>
|
||||
<button
|
||||
class="btn"
|
||||
:class="viewMode === 'list' ? 'btn-primary' : 'btn-outline-primary'"
|
||||
@click="viewMode = 'list'"
|
||||
>
|
||||
<b-icon-list></b-icon-list>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<!-- Grid View -->
|
||||
<div v-if="viewMode === 'grid'" class="row">
|
||||
<div v-for="(item, index) in items" :key="index" class="col-sm-6 col-md-4 col-lg-3 mb-3">
|
||||
<div class="card h-100">
|
||||
<img :src="item.image.processedUrl || item.image.preview" class="card-img-top item-thumb" :alt="item.details.name">
|
||||
<div class="card-body p-2">
|
||||
<h6 class="card-title mb-1">{{ item.details.name }}</h6>
|
||||
<p class="card-text small text-muted mb-1">{{ item.details.category || 'No category' }}</p>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<small class="text-muted">Qty: {{ item.details.quantity }}</small>
|
||||
<small v-if="item.details.estimated_value" class="text-success">${{ item.details.estimated_value }}</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- List View -->
|
||||
<div v-else class="table-responsive">
|
||||
<table class="table table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Image</th>
|
||||
<th>Name</th>
|
||||
<th>Category</th>
|
||||
<th>Quantity</th>
|
||||
<th>Location</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(item, index) in items" :key="index">
|
||||
<td>
|
||||
<img :src="item.image.processedUrl || item.image.preview" class="list-item-thumb" :alt="item.details.name">
|
||||
</td>
|
||||
<td class="fw-bold">{{ item.details.name }}</td>
|
||||
<td>
|
||||
<span v-if="item.details.category" class="badge bg-light text-dark">{{ item.details.category }}</span>
|
||||
<span v-else class="text-muted">-</span>
|
||||
</td>
|
||||
<td>{{ item.details.quantity }} {{ item.details.unit }}</td>
|
||||
<td>{{ item.details.location || '-' }}</td>
|
||||
<td>
|
||||
<span v-if="item.details.estimated_value" class="text-success">${{ item.details.estimated_value }}</span>
|
||||
<span v-else class="text-muted">-</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Final Action -->
|
||||
<div class="final-action text-center">
|
||||
<div class="card">
|
||||
<div class="card-body py-4">
|
||||
<b-icon-check-circle class="text-success mb-3" style="font-size: 3rem;"></b-icon-check-circle>
|
||||
<h5 class="mb-3">Ready to Complete Import</h5>
|
||||
<p class="text-muted mb-4">
|
||||
All {{ totalItems }} items have been processed and are ready to be added to your inventory.
|
||||
This action cannot be undone.
|
||||
</p>
|
||||
<div class="d-flex justify-content-center gap-3">
|
||||
<button class="btn btn-outline-secondary" @click="$emit('prev')">
|
||||
<b-icon-chevron-left class="me-1"></b-icon-chevron-left>
|
||||
Go Back
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-success btn-lg"
|
||||
@click="completeImport"
|
||||
:disabled="importing"
|
||||
>
|
||||
<div v-if="importing" class="spinner-border spinner-border-sm me-2" role="status"></div>
|
||||
<b-icon-check-circle v-else class="me-2"></b-icon-check-circle>
|
||||
{{ importing ? 'Importing...' : 'Complete Import' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as BIcons from "bootstrap-icons-vue";
|
||||
|
||||
export default {
|
||||
name: 'FotoFirstStep4',
|
||||
components: {
|
||||
...BIcons
|
||||
},
|
||||
props: {
|
||||
workflowInstance: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
step: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
payload: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
importing: false,
|
||||
viewMode: 'grid',
|
||||
importOptions: {
|
||||
generate_qr_codes: true,
|
||||
send_notification: true,
|
||||
create_report: true,
|
||||
auto_backup: false
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
items() {
|
||||
return this.payload.completed_items || [];
|
||||
},
|
||||
totalItems() {
|
||||
return this.items.length;
|
||||
},
|
||||
categorizedItems() {
|
||||
return this.items.filter(item => item.details.category).length;
|
||||
},
|
||||
itemsWithLocation() {
|
||||
return this.items.filter(item => item.details.location).length;
|
||||
},
|
||||
totalValue() {
|
||||
return this.items.reduce((sum, item) => {
|
||||
return sum + (item.details.estimated_value || 0);
|
||||
}, 0).toFixed(2);
|
||||
},
|
||||
categoryBreakdown() {
|
||||
const breakdown = {};
|
||||
this.items.forEach(item => {
|
||||
const category = item.details.category || 'uncategorized';
|
||||
breakdown[category] = (breakdown[category] || 0) + 1;
|
||||
});
|
||||
return breakdown;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// Load import options from payload
|
||||
if (this.payload.import_options) {
|
||||
this.importOptions = { ...this.importOptions, ...this.payload.import_options };
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async completeImport() {
|
||||
if (this.totalItems === 0) {
|
||||
alert('No items to import. Please go back and add items.');
|
||||
return;
|
||||
}
|
||||
|
||||
const confirmed = confirm(
|
||||
`Are you sure you want to import ${this.totalItems} items? This action cannot be undone.`
|
||||
);
|
||||
|
||||
if (!confirmed) return;
|
||||
|
||||
try {
|
||||
this.importing = true;
|
||||
|
||||
// Update payload with final options
|
||||
this.updatePayload();
|
||||
|
||||
// Simulate import process
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
|
||||
// Complete the workflow
|
||||
this.$emit('update', {
|
||||
import_completed: true,
|
||||
completion_timestamp: new Date().toISOString()
|
||||
});
|
||||
|
||||
// Navigate to success or trigger workflow completion
|
||||
this.$emit('complete');
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error completing import:', error);
|
||||
alert('Error completing import. Please try again.');
|
||||
} finally {
|
||||
this.importing = false;
|
||||
}
|
||||
},
|
||||
|
||||
updatePayload() {
|
||||
this.$emit('update', {
|
||||
import_options: this.importOptions,
|
||||
final_summary: {
|
||||
total_items: this.totalItems,
|
||||
categorized_items: this.categorizedItems,
|
||||
items_with_location: this.itemsWithLocation,
|
||||
total_value: parseFloat(this.totalValue),
|
||||
category_breakdown: this.categoryBreakdown
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.item-thumb {
|
||||
height: 120px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.list-item-thumb {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
object-fit: cover;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.final-action .card {
|
||||
border: 2px solid #28a745;
|
||||
background: linear-gradient(135deg, #f8fff8 0%, #e8f5e8 100%);
|
||||
}
|
||||
</style>
|
||||
Loading…
Add table
Add a link
Reference in a new issue