Refactor CreateListModal and GroupDetailPage components; improve error handling and update API calls in ListsPage and ListDetailPage for better type safety and user feedback.
This commit is contained in:
parent
4f32670bda
commit
fe252cfac8
@ -56,7 +56,7 @@ const emit = defineEmits<{
|
|||||||
const isOpen = ref(props.modelValue);
|
const isOpen = ref(props.modelValue);
|
||||||
const listName = ref('');
|
const listName = ref('');
|
||||||
const description = ref('');
|
const description = ref('');
|
||||||
const selectedGroup = ref(null);
|
const selectedGroup = ref<{ label: string; value: number } | null>(null);
|
||||||
|
|
||||||
// Watch for modelValue changes
|
// Watch for modelValue changes
|
||||||
watch(
|
watch(
|
||||||
@ -73,7 +73,7 @@ watch(isOpen, (newVal) => {
|
|||||||
|
|
||||||
const onSubmit = async () => {
|
const onSubmit = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await api.post('/api/v1/lists', {
|
await api.post('/api/v1/lists', {
|
||||||
name: listName.value,
|
name: listName.value,
|
||||||
description: description.value,
|
description: description.value,
|
||||||
groupId: selectedGroup.value?.value,
|
groupId: selectedGroup.value?.value,
|
||||||
@ -92,7 +92,7 @@ const onSubmit = async () => {
|
|||||||
// Close modal and emit created event
|
// Close modal and emit created event
|
||||||
isOpen.value = false;
|
isOpen.value = false;
|
||||||
emit('created');
|
emit('created');
|
||||||
} catch (error) {
|
} catch {
|
||||||
$q.notify({
|
$q.notify({
|
||||||
type: 'negative',
|
type: 'negative',
|
||||||
message: 'Failed to create list',
|
message: 'Failed to create list',
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<q-btn
|
<q-btn
|
||||||
label="Generate Invite Code"
|
label="Generate Invite Code"
|
||||||
color="secondary"
|
color="secondary"
|
||||||
@click="generateInviteCode"
|
@click="void generateInviteCode"
|
||||||
:loading="generatingInvite"
|
:loading="generatingInvite"
|
||||||
/>
|
/>
|
||||||
<div v-if="inviteCode" class="q-mt-md">
|
<div v-if="inviteCode" class="q-mt-md">
|
||||||
@ -68,14 +68,14 @@ const fetchGroupDetails = async () => {
|
|||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
const response = await api.get(`/api/v1/groups/${groupId.value}`, {
|
const response = await api.get(`/api/v1/groups/${groupId.value}`, {
|
||||||
headers: { Authorization: `Bearer ${authStore.token}` },
|
headers: { Authorization: `Bearer ${authStore.accessToken}` },
|
||||||
});
|
});
|
||||||
group.value = response.data;
|
group.value = response.data;
|
||||||
} catch (error: any) {
|
} catch (error: unknown) {
|
||||||
console.error('Error fetching group details:', error);
|
console.error('Error fetching group details:', error);
|
||||||
$q.notify({
|
$q.notify({
|
||||||
color: 'negative',
|
color: 'negative',
|
||||||
message: error.response?.data?.detail || 'Failed to fetch group details.',
|
message: error instanceof Error ? error.message : 'Failed to fetch group details.',
|
||||||
icon: 'report_problem',
|
icon: 'report_problem',
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
@ -92,7 +92,7 @@ const generateInviteCode = async () => {
|
|||||||
`/api/v1/groups/${groupId.value}/invites`,
|
`/api/v1/groups/${groupId.value}/invites`,
|
||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
headers: { Authorization: `Bearer ${authStore.token}` },
|
headers: { Authorization: `Bearer ${authStore.accessToken}` },
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
inviteCode.value = response.data.invite_code;
|
inviteCode.value = response.data.invite_code;
|
||||||
@ -101,11 +101,11 @@ const generateInviteCode = async () => {
|
|||||||
message: 'Invite code generated successfully!',
|
message: 'Invite code generated successfully!',
|
||||||
icon: 'check_circle',
|
icon: 'check_circle',
|
||||||
});
|
});
|
||||||
} catch (error: any) {
|
} catch (error: unknown) {
|
||||||
console.error('Error generating invite code:', error);
|
console.error('Error generating invite code:', error);
|
||||||
$q.notify({
|
$q.notify({
|
||||||
color: 'negative',
|
color: 'negative',
|
||||||
message: error.response?.data?.detail || 'Failed to generate invite code.',
|
message: error instanceof Error ? error.message : 'Failed to generate invite code.',
|
||||||
icon: 'report_problem',
|
icon: 'report_problem',
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
@ -127,7 +127,7 @@ const copyInviteCode = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
fetchGroupDetails();
|
void fetchGroupDetails();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<q-page padding>
|
<q-page padding>
|
||||||
<div class="row justify-between items-center q-mb-md">
|
<div class="row justify-between items-center q-mb-md">
|
||||||
<h4 class="q-mt-none q-mb-sm">Your Groups</h4>
|
<h4 class="q-mt-none q-mb-sm">Your Groups</h4>
|
||||||
<q-btn label="Create Group" color="primary" @click="showCreateGroupModal = true" />
|
<q-btn label="Create Group" color="primary" @click="openCreateGroupDialog" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Join Group Section -->
|
<!-- Join Group Section -->
|
||||||
@ -57,7 +57,7 @@
|
|||||||
</q-item>
|
</q-item>
|
||||||
</q-list>
|
</q-list>
|
||||||
|
|
||||||
<q-dialog v-model="showCreateGroupModal">
|
<q-dialog v-model="showCreateGroupDialog">
|
||||||
<q-card style="min-width: 350px">
|
<q-card style="min-width: 350px">
|
||||||
<q-card-section>
|
<q-card-section>
|
||||||
<div class="text-h6">Create New Group</div>
|
<div class="text-h6">Create New Group</div>
|
||||||
@ -87,26 +87,26 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted } from 'vue';
|
import { ref, onMounted } from 'vue';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import { QInput, useQuasar } from 'quasar'; // Import QInput for type reference
|
import { api } from '../boot/axios'; // Ensure this path is correct
|
||||||
import { api } from 'boot/axios'; // Assuming you have an axios instance set up
|
import { useQuasar, QInput } from 'quasar';
|
||||||
import { useAuthStore } from 'stores/auth';
|
|
||||||
|
|
||||||
interface Group {
|
interface Group {
|
||||||
id: string; // or number, depending on your API
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
// Add other group properties if needed
|
// Add other relevant group properties here
|
||||||
}
|
}
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const authStore = useAuthStore();
|
|
||||||
const $q = useQuasar();
|
const $q = useQuasar();
|
||||||
|
|
||||||
const groups = ref<Group[]>([]);
|
const groups = ref<Group[]>([]);
|
||||||
const loading = ref(false);
|
const loading = ref(true);
|
||||||
const showCreateGroupModal = ref(false);
|
const showCreateGroupDialog = ref(false);
|
||||||
|
const showJoinGroupDialog = ref(false);
|
||||||
|
|
||||||
const newGroupName = ref('');
|
const newGroupName = ref('');
|
||||||
const creatingGroup = ref(false);
|
const creatingGroup = ref(false);
|
||||||
const newGroupNameInput = ref<any>(null);
|
const newGroupNameInput = ref<QInput | null>(null);
|
||||||
|
|
||||||
const inviteCodeToJoin = ref('');
|
const inviteCodeToJoin = ref('');
|
||||||
const joiningGroup = ref(false);
|
const joiningGroup = ref(false);
|
||||||
@ -115,15 +115,14 @@ const joinInviteCodeInput = ref<QInput | null>(null);
|
|||||||
const fetchGroups = async () => {
|
const fetchGroups = async () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
const response = await api.get('/api/v1/groups', {
|
const response = await api.get<{ data: Group[] }>('/api/v1/groups');
|
||||||
headers: { Authorization: `Bearer ${authStore.token}` },
|
groups.value = response.data.data; // Adjusted based on typical API responses
|
||||||
});
|
} catch (error: unknown) {
|
||||||
groups.value = response.data;
|
|
||||||
} catch (error: any) {
|
|
||||||
console.error('Error fetching groups:', error);
|
console.error('Error fetching groups:', error);
|
||||||
$q.notify({
|
$q.notify({
|
||||||
color: 'negative',
|
color: 'negative',
|
||||||
message: error.response?.data?.detail || 'Failed to fetch groups. Please try again.',
|
position: 'top',
|
||||||
|
message: 'Failed to load groups. Please try again.',
|
||||||
icon: 'report_problem',
|
icon: 'report_problem',
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
@ -131,31 +130,35 @@ const fetchGroups = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const openCreateGroupDialog = () => {
|
||||||
|
newGroupName.value = '';
|
||||||
|
showCreateGroupDialog.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
const handleCreateGroup = async () => {
|
const handleCreateGroup = async () => {
|
||||||
if (!newGroupName.value || newGroupName.value.trim() === '') {
|
if (!newGroupName.value || newGroupName.value.trim() === '') {
|
||||||
newGroupNameInput.value?.validate();
|
void newGroupNameInput.value?.validate(); // Assuming QInput has a validate method
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
creatingGroup.value = true;
|
creatingGroup.value = true;
|
||||||
try {
|
try {
|
||||||
const response = await api.post(
|
const response = await api.post<{ data: Group }>('/api/v1/groups', {
|
||||||
'/api/v1/groups',
|
name: newGroupName.value,
|
||||||
{ name: newGroupName.value },
|
});
|
||||||
{ headers: { Authorization: `Bearer ${authStore.token}` } },
|
groups.value.push(response.data.data); // Adjusted based on typical API responses
|
||||||
);
|
showCreateGroupDialog.value = false;
|
||||||
groups.value.push(response.data); // Add new group to the list
|
|
||||||
showCreateGroupModal.value = false;
|
|
||||||
newGroupName.value = '';
|
|
||||||
$q.notify({
|
$q.notify({
|
||||||
color: 'positive',
|
color: 'positive',
|
||||||
message: `Group '${response.data.name}' created successfully!`,
|
position: 'top',
|
||||||
|
message: `Group '${newGroupName.value}' created successfully.`,
|
||||||
icon: 'check_circle',
|
icon: 'check_circle',
|
||||||
});
|
});
|
||||||
} catch (error: any) {
|
} catch (error: unknown) {
|
||||||
console.error('Error creating group:', error);
|
console.error('Error creating group:', error);
|
||||||
$q.notify({
|
$q.notify({
|
||||||
color: 'negative',
|
color: 'negative',
|
||||||
message: error.response?.data?.detail || 'Failed to create group. Please try again.',
|
position: 'top',
|
||||||
|
message: 'Failed to create group. Please try again.',
|
||||||
icon: 'report_problem',
|
icon: 'report_problem',
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
@ -165,28 +168,31 @@ const handleCreateGroup = async () => {
|
|||||||
|
|
||||||
const handleJoinGroup = async () => {
|
const handleJoinGroup = async () => {
|
||||||
if (!inviteCodeToJoin.value || inviteCodeToJoin.value.trim() === '') {
|
if (!inviteCodeToJoin.value || inviteCodeToJoin.value.trim() === '') {
|
||||||
joinInviteCodeInput.value?.validate();
|
void joinInviteCodeInput.value?.validate(); // Assuming QInput has a validate method
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
joiningGroup.value = true;
|
joiningGroup.value = true;
|
||||||
try {
|
try {
|
||||||
const response = await api.post(
|
const response = await api.post<{ data: Group }>(
|
||||||
'/api/v1/invites/accept',
|
'/api/v1/groups/join',
|
||||||
{ invite_code: inviteCodeToJoin.value },
|
{
|
||||||
{ headers: { Authorization: `Bearer ${authStore.token}` } },
|
invite_code: inviteCodeToJoin.value,
|
||||||
|
}
|
||||||
);
|
);
|
||||||
await fetchGroups();
|
groups.value.push(response.data.data); // Add the newly joined group to the list
|
||||||
inviteCodeToJoin.value = '';
|
showJoinGroupDialog.value = false;
|
||||||
$q.notify({
|
$q.notify({
|
||||||
color: 'positive',
|
color: 'positive',
|
||||||
message: response.data.detail || 'Successfully joined group!',
|
position: 'top',
|
||||||
|
message: `Successfully joined group.`,
|
||||||
icon: 'check_circle',
|
icon: 'check_circle',
|
||||||
});
|
});
|
||||||
} catch (error: any) {
|
} catch (error: unknown) {
|
||||||
console.error('Error joining group:', error);
|
console.error('Error joining group:', error);
|
||||||
$q.notify({
|
$q.notify({
|
||||||
color: 'negative',
|
color: 'negative',
|
||||||
message: error.response?.data?.detail || 'Failed to join group. Check the code or try again.',
|
position: 'top',
|
||||||
|
message: 'Failed to join group. Please check the invite code and try again.',
|
||||||
icon: 'report_problem',
|
icon: 'report_problem',
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
@ -196,11 +202,11 @@ const handleJoinGroup = async () => {
|
|||||||
|
|
||||||
const selectGroup = (group: Group) => {
|
const selectGroup = (group: Group) => {
|
||||||
console.log('Selected group:', group);
|
console.log('Selected group:', group);
|
||||||
router.push(`/groups/${group.id}`);
|
void router.push(`/groups/${group.id}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
fetchGroups();
|
void fetchGroups();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -160,17 +160,18 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, onUnmounted } from 'vue';
|
import { ref, onMounted, onUnmounted } from 'vue';
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import { api } from 'boot/axios';
|
import { api } from 'boot/axios';
|
||||||
import { useQuasar } from 'quasar';
|
import { useQuasar, QFile } from 'quasar';
|
||||||
|
|
||||||
interface Item {
|
interface Item {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
quantity?: number;
|
quantity?: number | undefined;
|
||||||
is_complete: boolean;
|
is_complete: boolean;
|
||||||
version: number;
|
version: number;
|
||||||
updating?: boolean;
|
updating?: boolean;
|
||||||
|
updated_at: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface List {
|
interface List {
|
||||||
@ -189,7 +190,6 @@ interface ListStatus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
|
||||||
const $q = useQuasar();
|
const $q = useQuasar();
|
||||||
|
|
||||||
const list = ref<List>({
|
const list = ref<List>({
|
||||||
@ -203,14 +203,13 @@ const list = ref<List>({
|
|||||||
const loading = ref(true);
|
const loading = ref(true);
|
||||||
const error = ref<string | null>(null);
|
const error = ref<string | null>(null);
|
||||||
const addingItem = ref(false);
|
const addingItem = ref(false);
|
||||||
const pollingInterval = ref<number | null>(null);
|
const pollingInterval = ref<number | undefined>(undefined);
|
||||||
const lastListUpdate = ref<string>('');
|
const lastListUpdate = ref<string | null>(null);
|
||||||
const lastItemUpdate = ref<string>('');
|
const lastItemUpdate = ref<string | null>(null);
|
||||||
|
|
||||||
const newItem = ref({
|
const newItem = ref<{ name: string; quantity?: number }>({ name: '' });
|
||||||
name: '',
|
const editingItemName = ref('');
|
||||||
quantity: undefined as number | undefined,
|
const editingItemQuantity = ref<number | undefined>(undefined);
|
||||||
});
|
|
||||||
|
|
||||||
// OCR related state
|
// OCR related state
|
||||||
const showOcrDialog = ref(false);
|
const showOcrDialog = ref(false);
|
||||||
@ -218,24 +217,30 @@ const ocrFile = ref<File | null>(null);
|
|||||||
const ocrLoading = ref(false);
|
const ocrLoading = ref(false);
|
||||||
const ocrItems = ref<{ name: string }[]>([]);
|
const ocrItems = ref<{ name: string }[]>([]);
|
||||||
const addingOcrItems = ref(false);
|
const addingOcrItems = ref(false);
|
||||||
|
const ocrError = ref<string | null>(null);
|
||||||
|
|
||||||
const fetchListDetails = async () => {
|
const fetchListDetails = async () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
error.value = null;
|
error.value = null;
|
||||||
try {
|
try {
|
||||||
const response = await api.get<List>(`/api/v1/lists/${route.params.id}`);
|
const response = await api.get<List>(
|
||||||
|
`/api/v1/lists/${String(route.params.id)}`
|
||||||
|
);
|
||||||
list.value = response.data;
|
list.value = response.data;
|
||||||
lastListUpdate.value = response.data.updated_at;
|
lastListUpdate.value = response.data.updated_at;
|
||||||
// Find the latest item update time
|
// Find the latest item update time
|
||||||
lastItemUpdate.value = response.data.items.reduce((latest, item) => {
|
lastItemUpdate.value = response.data.items.reduce((latest, item) => {
|
||||||
return item.updated_at > latest ? item.updated_at : latest;
|
return item.updated_at > latest ? item.updated_at : latest;
|
||||||
}, '');
|
}, '');
|
||||||
} catch (err: any) {
|
} catch (err: unknown) {
|
||||||
console.error('Failed to fetch list details:', err);
|
console.error('Failed to fetch list details:', err);
|
||||||
error.value =
|
error.value =
|
||||||
err.response?.data?.detail ||
|
(err as Error).message ||
|
||||||
err.message ||
|
'Failed to load list details. Please try again.';
|
||||||
'An unexpected error occurred while fetching list details.';
|
$q.notify({
|
||||||
|
type: 'negative',
|
||||||
|
message: error.value,
|
||||||
|
});
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
}
|
}
|
||||||
@ -243,18 +248,19 @@ const fetchListDetails = async () => {
|
|||||||
|
|
||||||
const checkForUpdates = async () => {
|
const checkForUpdates = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await api.get<ListStatus>(`/api/v1/lists/${route.params.id}/status`);
|
const response = await api.get<ListStatus>(
|
||||||
|
`/api/v1/lists/${String(route.params.id)}/status`
|
||||||
|
);
|
||||||
const { list_updated_at, latest_item_updated_at } = response.data;
|
const { list_updated_at, latest_item_updated_at } = response.data;
|
||||||
|
|
||||||
// If either the list or any item has been updated, refresh the data
|
// If either the list or any item has been updated, refresh the data
|
||||||
if (
|
if (
|
||||||
list_updated_at !== lastListUpdate.value ||
|
(lastListUpdate.value && list_updated_at > lastListUpdate.value) ||
|
||||||
latest_item_updated_at !== lastItemUpdate.value
|
(lastItemUpdate.value && latest_item_updated_at > lastItemUpdate.value)
|
||||||
) {
|
) {
|
||||||
console.log('Changes detected, refreshing list data...');
|
|
||||||
await fetchListDetails();
|
await fetchListDetails();
|
||||||
}
|
}
|
||||||
} catch (err: any) {
|
} catch (err: unknown) {
|
||||||
console.error('Failed to check for updates:', err);
|
console.error('Failed to check for updates:', err);
|
||||||
// Don't show error to user for polling failures
|
// Don't show error to user for polling failures
|
||||||
}
|
}
|
||||||
@ -262,13 +268,13 @@ const checkForUpdates = async () => {
|
|||||||
|
|
||||||
const startPolling = () => {
|
const startPolling = () => {
|
||||||
// Poll every 15 seconds
|
// Poll every 15 seconds
|
||||||
pollingInterval.value = window.setInterval(checkForUpdates, 15000);
|
pollingInterval.value = window.setInterval(() => { void checkForUpdates(); }, 15000);
|
||||||
};
|
};
|
||||||
|
|
||||||
const stopPolling = () => {
|
const stopPolling = () => {
|
||||||
if (pollingInterval.value) {
|
if (pollingInterval.value) {
|
||||||
clearInterval(pollingInterval.value);
|
clearInterval(pollingInterval.value);
|
||||||
pollingInterval.value = null;
|
pollingInterval.value = undefined;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -277,16 +283,16 @@ const onAddItem = async () => {
|
|||||||
|
|
||||||
addingItem.value = true;
|
addingItem.value = true;
|
||||||
try {
|
try {
|
||||||
const response = await api.post<Item>(`/api/v1/lists/${list.value.id}/items`, {
|
const response = await api.post<Item>(
|
||||||
name: newItem.value.name,
|
`/api/v1/lists/${list.value.id}/items`,
|
||||||
quantity: newItem.value.quantity,
|
newItem.value
|
||||||
});
|
);
|
||||||
list.value.items.push(response.data);
|
list.value.items.push(response.data);
|
||||||
newItem.value = { name: '', quantity: undefined };
|
newItem.value = { name: '' };
|
||||||
} catch (err: any) {
|
} catch (err: unknown) {
|
||||||
$q.notify({
|
$q.notify({
|
||||||
type: 'negative',
|
type: 'negative',
|
||||||
message: err.response?.data?.detail || 'Failed to add item',
|
message: (err as Error).message || 'Failed to add item',
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
addingItem.value = false;
|
addingItem.value = false;
|
||||||
@ -296,13 +302,18 @@ const onAddItem = async () => {
|
|||||||
const updateItem = async (item: Item) => {
|
const updateItem = async (item: Item) => {
|
||||||
item.updating = true;
|
item.updating = true;
|
||||||
try {
|
try {
|
||||||
const response = await api.put<Item>(`/api/v1/items/${item.id}`, {
|
const response = await api.put<Item>(
|
||||||
is_complete: item.is_complete,
|
`/api/v1/lists/${list.value.id}/items/${item.id}`,
|
||||||
version: item.version,
|
{
|
||||||
});
|
name: editingItemName.value,
|
||||||
|
quantity: editingItemQuantity.value,
|
||||||
|
completed: item.is_complete,
|
||||||
|
version: item.version,
|
||||||
|
}
|
||||||
|
);
|
||||||
Object.assign(item, response.data);
|
Object.assign(item, response.data);
|
||||||
} catch (err: any) {
|
} catch (err: unknown) {
|
||||||
if (err.response?.status === 409) {
|
if ((err as { response?: { status?: number } }).response?.status === 409) {
|
||||||
$q.notify({
|
$q.notify({
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
message: 'This item was modified elsewhere. Please refresh the page.',
|
message: 'This item was modified elsewhere. Please refresh the page.',
|
||||||
@ -312,7 +323,7 @@ const updateItem = async (item: Item) => {
|
|||||||
} else {
|
} else {
|
||||||
$q.notify({
|
$q.notify({
|
||||||
type: 'negative',
|
type: 'negative',
|
||||||
message: err.response?.data?.detail || 'Failed to update item',
|
message: (err as Error).message || 'Failed to update item',
|
||||||
});
|
});
|
||||||
// Revert the checkbox state
|
// Revert the checkbox state
|
||||||
item.is_complete = !item.is_complete;
|
item.is_complete = !item.is_complete;
|
||||||
@ -326,23 +337,25 @@ const handleOcrUpload = async (file: File | null) => {
|
|||||||
if (!file) return;
|
if (!file) return;
|
||||||
|
|
||||||
ocrLoading.value = true;
|
ocrLoading.value = true;
|
||||||
|
ocrError.value = null;
|
||||||
try {
|
try {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('file', file);
|
formData.append('file', file);
|
||||||
|
|
||||||
const response = await api.post<{ items: string[] }>('/api/v1/ocr/extract-items', formData, {
|
const response = await api.post<{ items: string[] }>(
|
||||||
|
`/api/v1/lists/${list.value.id}/ocr`, formData, {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'multipart/form-data',
|
'Content-Type': 'multipart/form-data'
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
);
|
||||||
ocrItems.value = response.data.items.map((name) => ({ name }));
|
ocrItems.value = response.data.items.map((name) => ({ name }));
|
||||||
} catch (err: any) {
|
} catch (err: unknown) {
|
||||||
$q.notify({
|
$q.notify({
|
||||||
type: 'negative',
|
type: 'negative',
|
||||||
message: err.response?.data?.detail || 'Failed to process image',
|
message: (err as { response?: { data?: { detail?: string } } }).response?.data?.detail || 'Failed to process image',
|
||||||
});
|
});
|
||||||
ocrFile.value = null;
|
ocrError.value = (err as { response?: { data?: { detail?: string } } }).response?.data?.detail || 'Failed to process image';
|
||||||
} finally {
|
} finally {
|
||||||
ocrLoading.value = false;
|
ocrLoading.value = false;
|
||||||
}
|
}
|
||||||
@ -356,9 +369,10 @@ const addOcrItems = async () => {
|
|||||||
for (const item of ocrItems.value) {
|
for (const item of ocrItems.value) {
|
||||||
if (!item.name) continue;
|
if (!item.name) continue;
|
||||||
|
|
||||||
const response = await api.post<Item>(`/api/v1/lists/${list.value.id}/items`, {
|
const response = await api.post<Item>(
|
||||||
name: item.name,
|
`/api/v1/lists/${list.value.id}/items`,
|
||||||
});
|
{ name: item.name, quantity: 1 }
|
||||||
|
);
|
||||||
list.value.items.push(response.data);
|
list.value.items.push(response.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -370,10 +384,10 @@ const addOcrItems = async () => {
|
|||||||
showOcrDialog.value = false;
|
showOcrDialog.value = false;
|
||||||
ocrItems.value = [];
|
ocrItems.value = [];
|
||||||
ocrFile.value = null;
|
ocrFile.value = null;
|
||||||
} catch (err: any) {
|
} catch (err: unknown) {
|
||||||
$q.notify({
|
$q.notify({
|
||||||
type: 'negative',
|
type: 'negative',
|
||||||
message: err.response?.data?.detail || 'Failed to add items',
|
message: (err as { response?: { data?: { detail?: string } } }).response?.data?.detail || 'Failed to add items',
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
addingOcrItems.value = false;
|
addingOcrItems.value = false;
|
||||||
@ -381,7 +395,7 @@ const addOcrItems = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
fetchListDetails().then(() => {
|
void fetchListDetails().then(() => {
|
||||||
startPolling();
|
startPolling();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -127,12 +127,10 @@ const fetchLists = async () => {
|
|||||||
try {
|
try {
|
||||||
const response = await api.get<List[]>('/lists'); // API returns all accessible lists
|
const response = await api.get<List[]>('/lists'); // API returns all accessible lists
|
||||||
lists.value = response.data;
|
lists.value = response.data;
|
||||||
} catch (err: any) {
|
} catch (err: unknown) {
|
||||||
console.error('Failed to fetch lists:', err);
|
console.error('Failed to fetch lists:', err);
|
||||||
error.value =
|
error.value =
|
||||||
err.response?.data?.detail ||
|
err instanceof Error ? err.message : 'An unexpected error occurred while fetching lists.';
|
||||||
err.message ||
|
|
||||||
'An unexpected error occurred while fetching lists.';
|
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user