stash
This commit is contained in:
parent
d606de8773
commit
2218cc3543
12 changed files with 393 additions and 2204 deletions
|
|
@ -7,6 +7,8 @@
|
|||
<p class="text-muted">Upload your CSV or Excel file containing item data for bulk import.</p>
|
||||
</div>
|
||||
|
||||
<div v-if="uploadError" class="alert alert-danger">{{ uploadError }}</div>
|
||||
|
||||
<!-- File Upload Area -->
|
||||
<div class="upload-section mb-4">
|
||||
<div class="card">
|
||||
|
|
@ -321,6 +323,7 @@ export default {
|
|||
processingFile: false,
|
||||
processingStatus: '',
|
||||
isDragOver: false,
|
||||
uploadError: null,
|
||||
fileAnalysis: null,
|
||||
detectedColumns: [],
|
||||
parsedRows: [],
|
||||
|
|
@ -385,8 +388,9 @@ export default {
|
|||
},
|
||||
|
||||
async processFile(file) {
|
||||
this.uploadError = null;
|
||||
if (!this.isValidFileType(file)) {
|
||||
alert('Please upload a CSV or Excel file (.csv, .xlsx, .xls)');
|
||||
this.uploadError = 'Please upload a CSV or Excel file (.csv, .xlsx, .xls)';
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -406,7 +410,7 @@ export default {
|
|||
this.updateStep1Payload();
|
||||
} catch (error) {
|
||||
console.error('Error processing file:', error);
|
||||
alert('Error processing file. Please try again.');
|
||||
this.uploadError = 'Error processing file. Please try again.';
|
||||
this.removeFile();
|
||||
} finally {
|
||||
this.processingFile = false;
|
||||
|
|
@ -565,10 +569,11 @@ export default {
|
|||
|
||||
proceedFromStep1() {
|
||||
if (!this.canProceedFromStep1) {
|
||||
alert('Please upload a file and map the required Name column before proceeding.');
|
||||
this.uploadError = 'Please upload a file and map the required Name column before proceeding.';
|
||||
return;
|
||||
}
|
||||
|
||||
this.uploadError = null;
|
||||
this.updateStep1Payload();
|
||||
this.$emit('next');
|
||||
},
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -184,7 +184,7 @@ const routes = [{path: '/', component: Dashboard, meta: {requiresAuth: true}}, {
|
|||
}, {path: '/workflows',
|
||||
component: Workflows,
|
||||
meta: {requiresAuth: true},
|
||||
}, {path: '/workflows/:id/:phase?',
|
||||
}, {path: '/workflows/:id/:step?',
|
||||
name: 'workflow-detail',
|
||||
component: WorkflowDetail,
|
||||
meta: {requiresAuth: true},
|
||||
|
|
|
|||
|
|
@ -426,9 +426,9 @@ export default createStore({
|
|||
? await dispatch('getFriendServers', {username: 'x@' + splitGroupHandle(item.owner_group).domain})
|
||||
: await dispatch('getHomeServers')
|
||||
const path = '/api/v1/inventory_items/' + encodeHandleForUrl(item.owner_group || item.owner) + '/' + item.id + '/'
|
||||
const ret = await servers.delete(getters.signAuth, path)
|
||||
dispatch('fetchInventoryItems')
|
||||
return ret
|
||||
// No self-refetch here, same as createInventoryItem/updateInventoryItem - callers
|
||||
// refetch whichever owner-scoped list (own/group/friend) is actually on screen.
|
||||
return await servers.delete(getters.signAuth, path)
|
||||
},
|
||||
async fetchSearchResults({state, commit, dispatch, getters}, {query}) {
|
||||
const servers = await dispatch('getAllKnownServers')
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@
|
|||
<h5 class="card-title">Images</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<span v-for="files in only_images(files)" :key="files.id">
|
||||
<deletable-wrapper @delete="deleteFile(files)">
|
||||
<authenticated-image :src="files.name" :owner="files.owner"
|
||||
<span v-for="file in only_images(files)" :key="file.id">
|
||||
<deletable-wrapper @delete="deleteFile(file)">
|
||||
<authenticated-image :src="thumbnailPathForHash(file.hash)" :owner="file.owner"
|
||||
class="img-thumbnail"/>
|
||||
</deletable-wrapper>
|
||||
</span>
|
||||
|
|
@ -58,6 +58,10 @@ export default {
|
|||
},
|
||||
whithout_images(files) {
|
||||
return files.filter(file => !file.mime_type.startsWith("image/"));
|
||||
},
|
||||
// See docs/implementation.md#thumbnail-lookup-by-hash.
|
||||
thumbnailPathForHash(hash, size = 256) {
|
||||
return `/media/${size}/${hash.slice(0, 2)}/${hash.slice(2, 4)}/${hash.slice(4, 6)}/${hash.slice(6)}/`;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,10 @@
|
|||
<main class="content">
|
||||
<div class="container-fluid p-0">
|
||||
<h1 class="h3 mb-3">Settings</h1>
|
||||
<div v-if="deleteError" class="alert alert-danger">{{ deleteError }}</div>
|
||||
<div v-if="deleteSuccess" class="alert alert-success">
|
||||
Your account has been permanently deleted. Signing you out...
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-xl-2">
|
||||
<div class="card">
|
||||
|
|
@ -56,6 +60,12 @@ import {mapActions, mapGetters, mapMutations} from "vuex";
|
|||
export default {
|
||||
name: 'Settings',
|
||||
components: {BaseLayout},
|
||||
data() {
|
||||
return {
|
||||
deleteError: null,
|
||||
deleteSuccess: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['signAuth'])
|
||||
},
|
||||
|
|
@ -63,6 +73,7 @@ export default {
|
|||
...mapActions(['getHomeServers']),
|
||||
...mapMutations(['logout']),
|
||||
async deleteAccount() {
|
||||
this.deleteError = null;
|
||||
if (!confirm('Are you sure you want to permanently delete your account? ' +
|
||||
'All your data will be deleted and you will not be able to log back in. This cannot be undone.')) {
|
||||
return;
|
||||
|
|
@ -72,14 +83,16 @@ export default {
|
|||
const response = await servers.delete(this.signAuth, '/api/v1/account/');
|
||||
if (!response || !response.ok) {
|
||||
const errorBody = response ? await response.json().catch(() => ({})) : {};
|
||||
alert('Account deletion failed: ' + (errorBody.detail || response?.statusText || 'unknown error'));
|
||||
this.deleteError = 'Account deletion failed: ' + (errorBody.detail || response?.statusText || 'unknown error');
|
||||
return;
|
||||
}
|
||||
alert('Your account has been permanently deleted.');
|
||||
this.logout();
|
||||
this.deleteSuccess = true;
|
||||
// logout() redirects to /login right away - give the user a moment to read the
|
||||
// confirmation above before that navigation happens.
|
||||
setTimeout(() => this.logout(), 2000);
|
||||
} catch (error) {
|
||||
console.error('Account deletion failed', error);
|
||||
alert('Account deletion failed: ' + error);
|
||||
this.deleteError = 'Account deletion failed: ' + error;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,8 @@
|
|||
<br>
|
||||
<small>Delete all your data including photos, videos,
|
||||
comments, profile information and more</small>
|
||||
<div v-if="deleteError" class="alert alert-danger mt-3 mb-0">{{ deleteError }}</div>
|
||||
<div v-if="deleteSuccess" class="alert alert-success mt-3 mb-0">{{ deleteSuccess }}</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
@ -64,6 +66,8 @@
|
|||
</div>
|
||||
<br>
|
||||
<small>Import data from backup or other instances</small>
|
||||
<div v-if="importError" class="alert alert-danger mt-3 mb-0">{{ importError }}</div>
|
||||
<div v-if="importSuccess" class="alert alert-success mt-3 mb-0">{{ importSuccess }}</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
@ -82,7 +86,11 @@ export default {
|
|||
components: {},
|
||||
data: () => ({
|
||||
selectedFile: null,
|
||||
localUserIdentityRecord: null
|
||||
localUserIdentityRecord: null,
|
||||
deleteError: null,
|
||||
deleteSuccess: null,
|
||||
importError: null,
|
||||
importSuccess: null
|
||||
}),
|
||||
computed: {
|
||||
...mapGetters(['signAuth'])
|
||||
|
|
@ -96,6 +104,8 @@ export default {
|
|||
}
|
||||
},
|
||||
async deleteData() {
|
||||
this.deleteError = null;
|
||||
this.deleteSuccess = null;
|
||||
if (!confirm('Are you sure you want to permanently delete all your data (inventory, locations, ' +
|
||||
'settings, friends and files)? Your account itself will stay - this cannot be undone.')) {
|
||||
return;
|
||||
|
|
@ -105,19 +115,19 @@ export default {
|
|||
const response = await servers.delete(this.signAuth, '/api/v1/account_data/');
|
||||
if (!response || !response.ok) {
|
||||
const errorBody = response ? await response.json().catch(() => ({})) : {};
|
||||
alert('Data deletion failed: ' + (errorBody.detail || response?.statusText || 'unknown error'));
|
||||
this.deleteError = 'Data deletion failed: ' + (errorBody.detail || response?.statusText || 'unknown error');
|
||||
return;
|
||||
}
|
||||
const summary = await response.json().catch(() => ({}));
|
||||
alert('All your data has been deleted: ' +
|
||||
this.deleteSuccess = 'All your data has been deleted: ' +
|
||||
`${summary.inventory_items || 0} inventory items, ` +
|
||||
`${summary.locations || 0} locations, ` +
|
||||
`${summary.settings || 0} settings, ` +
|
||||
`${summary.friends || 0} friends, ` +
|
||||
`${summary.files || 0} files.`);
|
||||
`${summary.files || 0} files.`;
|
||||
} catch (error) {
|
||||
console.error('Data deletion failed', error);
|
||||
alert('Data deletion failed: ' + error);
|
||||
this.deleteError = 'Data deletion failed: ' + error;
|
||||
}
|
||||
},
|
||||
exportKey() {
|
||||
|
|
@ -141,8 +151,10 @@ export default {
|
|||
});
|
||||
},
|
||||
async importData() {
|
||||
this.importError = null;
|
||||
this.importSuccess = null;
|
||||
if (!this.selectedFile) {
|
||||
alert('Please select a file to import');
|
||||
this.importError = 'Please select a file to import';
|
||||
return;
|
||||
}
|
||||
try {
|
||||
|
|
@ -150,16 +162,16 @@ export default {
|
|||
const servers = await this.getHomeServers();
|
||||
const summary = await servers.post(this.signAuth, '/api/v1/import/', {zip: base64});
|
||||
if (summary && summary.detail) {
|
||||
alert('Data import failed: ' + summary.detail);
|
||||
this.importError = 'Data import failed: ' + summary.detail;
|
||||
return;
|
||||
}
|
||||
alert('Data imported successfully: ' +
|
||||
this.importSuccess = 'Data imported successfully: ' +
|
||||
`${summary.profile ? 'profile, ' : ''}` +
|
||||
`${summary.settings || 0} settings, ` +
|
||||
`${summary.inventory_items || 0} inventory items, ` +
|
||||
`${summary.friends || 0} friends, ` +
|
||||
`${summary.locations || 0} locations, ` +
|
||||
`${summary.files || 0} files.`);
|
||||
`${summary.files || 0} files.`;
|
||||
this.selectedFile = null;
|
||||
const fileInput = document.getElementById('inputFile');
|
||||
if (fileInput) {
|
||||
|
|
@ -167,7 +179,7 @@ export default {
|
|||
}
|
||||
} catch (error) {
|
||||
console.error('Data import failed', error);
|
||||
alert('Data import failed: ' + error);
|
||||
this.importError = 'Data import failed: ' + error;
|
||||
}
|
||||
},
|
||||
async exportData() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue