mirror of
https://github.com/KevinMidboe/seasoned.git
synced 2026-04-25 01:03:36 +00:00
Refactor data management with browser and server storage sections
- Split LocalStorageManager into modular StorageManager component - Create StorageSectionBrowser for localStorage/sessionStorage/cookies UI - Create StorageSectionServer for server-side data management (mock) - Extract ExportSection component from DataExport - Add storage type icons (IconCookie, IconDatabase, IconTimer) - Implement collapsible storage sections with visual indicators - Add colored borders and gradients per storage type - Display item counts and total size in section headers - Improve delete button layout using CSS Grid - Reduce DataExport from ~824 lines to focused component
This commit is contained in:
215
src/components/settings/StorageManager.vue
Normal file
215
src/components/settings/StorageManager.vue
Normal file
@@ -0,0 +1,215 @@
|
||||
<template>
|
||||
<div class="storage-manager">
|
||||
<StorageSectionBrowser
|
||||
:sections="storageSections"
|
||||
@clear-item="clearItem"
|
||||
/>
|
||||
|
||||
<DangerZoneAction
|
||||
title="Clear All Browser Data"
|
||||
description="Remove all locally stored data at once. This includes preferences, history, and cached information."
|
||||
button-text="Clear All Data"
|
||||
@action="clearAllData"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, inject } from "vue";
|
||||
import IconCookie from "@/icons/IconCookie.vue";
|
||||
import IconDatabase from "@/icons/IconDatabase.vue";
|
||||
import IconTimer from "@/icons/IconTimer.vue";
|
||||
import StorageSectionBrowser from "./StorageSectionBrowser.vue";
|
||||
import DangerZoneAction from "./DangerZoneAction.vue";
|
||||
import { formatBytes } from "../../utils";
|
||||
|
||||
interface StorageItem {
|
||||
key: string;
|
||||
description: string;
|
||||
size: string;
|
||||
type: "local" | "session" | "cookie";
|
||||
}
|
||||
|
||||
const notifications: {
|
||||
success: (options: {
|
||||
title: string;
|
||||
description?: string;
|
||||
timeout?: number;
|
||||
}) => void;
|
||||
error: (options: {
|
||||
title: string;
|
||||
description?: string;
|
||||
timeout?: number;
|
||||
}) => void;
|
||||
} = inject("notifications");
|
||||
|
||||
const dict = {
|
||||
commandPalette_stats: "Usage statistics for command palette navigation",
|
||||
"theme-preference": "Your selected color theme",
|
||||
plex_user_data: "Cached Plex account information",
|
||||
plex_library_data: "Cached Plex library details per section",
|
||||
plex_server_data: "Cached Plex server information",
|
||||
plex_library_last_sync: "UTC time string for last synced Plex data",
|
||||
plex_auth_token: "Authorized token from Plex.tv",
|
||||
authorization: "This sites user login token"
|
||||
};
|
||||
|
||||
const storageItems = computed<StorageItem[]>(() => {
|
||||
const items: StorageItem[] = [];
|
||||
|
||||
// local storage
|
||||
Object.keys(localStorage).map(key => {
|
||||
items.push({
|
||||
key,
|
||||
description: dict[key] ?? "",
|
||||
size: formatBytes(localStorage[key]?.length || 0),
|
||||
type: "local"
|
||||
});
|
||||
});
|
||||
|
||||
// session storage
|
||||
Object.keys(sessionStorage).map(key => {
|
||||
items.push({
|
||||
key,
|
||||
description: dict[key] ?? "",
|
||||
size: formatBytes(sessionStorage[key]?.length || 0),
|
||||
type: "session"
|
||||
});
|
||||
});
|
||||
|
||||
// cookies
|
||||
if (document.cookie) {
|
||||
document.cookie.split(";").forEach(cookie => {
|
||||
const [key, _] = cookie.trim().split("=");
|
||||
if (key) {
|
||||
items.push({
|
||||
key,
|
||||
description: dict[key] ?? "",
|
||||
size: formatBytes(cookie.length || 0),
|
||||
type: "cookie"
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return items;
|
||||
});
|
||||
|
||||
const getTotalSize = (items: StorageItem[]) => {
|
||||
const totalBytes = items.reduce((acc, item) => {
|
||||
const match = item.size.match(/^([\d.]+)\s*(\w+)$/);
|
||||
if (!match) return acc;
|
||||
const value = parseFloat(match[1]);
|
||||
const unit = match[2];
|
||||
return (
|
||||
acc +
|
||||
(unit === "KB"
|
||||
? value * 1024
|
||||
: unit === "MB"
|
||||
? value * 1024 * 1024
|
||||
: value)
|
||||
);
|
||||
}, 0);
|
||||
return formatBytes(totalBytes);
|
||||
};
|
||||
|
||||
const storageSections = computed(() => [
|
||||
{
|
||||
type: "local" as const,
|
||||
title: "LocalStorage",
|
||||
iconComponent: IconDatabase,
|
||||
description:
|
||||
"LocalStorage keeps data permanently on your device, even after closing your browser. It's used to remember your preferences and settings between visits.",
|
||||
items: storageItems.value.filter(item => item.type === "local"),
|
||||
get totalSize() {
|
||||
return getTotalSize(this.items);
|
||||
}
|
||||
},
|
||||
{
|
||||
type: "session" as const,
|
||||
title: "SessionStorage",
|
||||
iconComponent: IconTimer,
|
||||
description:
|
||||
"SessionStorage keeps data temporarily while you browse. It's automatically cleared when you close your browser tab or window.",
|
||||
items: storageItems.value.filter(item => item.type === "session"),
|
||||
get totalSize() {
|
||||
return getTotalSize(this.items);
|
||||
}
|
||||
},
|
||||
{
|
||||
type: "cookie" as const,
|
||||
title: "Cookies",
|
||||
iconComponent: IconCookie,
|
||||
description:
|
||||
"Cookies are small text files stored by your browser. They can be temporary (session cookies) or persistent, and are often used for authentication and tracking your activity.",
|
||||
items: storageItems.value.filter(item => item.type === "cookie"),
|
||||
get totalSize() {
|
||||
return getTotalSize(this.items);
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
function clearItem(key: string, type: "local" | "session" | "cookie") {
|
||||
try {
|
||||
if (type === "local") {
|
||||
localStorage.removeItem(key);
|
||||
} else if (type === "session") {
|
||||
sessionStorage.removeItem(key);
|
||||
} else if (type === "cookie") {
|
||||
document.cookie = `${key}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;
|
||||
}
|
||||
|
||||
notifications.success({
|
||||
title: "Data Cleared",
|
||||
description: `${key} has been cleared`,
|
||||
timeout: 3000
|
||||
});
|
||||
|
||||
// Force re-render
|
||||
storageItems.value;
|
||||
} catch (error) {
|
||||
notifications.error({
|
||||
title: "Error",
|
||||
description: `Failed to clear ${key}`,
|
||||
timeout: 5000
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function clearAllData() {
|
||||
const confirmed = confirm(
|
||||
"Are you sure you want to clear all locally stored data? This action cannot be undone."
|
||||
);
|
||||
|
||||
if (!confirmed) return;
|
||||
|
||||
try {
|
||||
localStorage.clear();
|
||||
sessionStorage.clear();
|
||||
document.cookie.split(";").forEach(cookie => {
|
||||
const eqPos = cookie.indexOf("=");
|
||||
const name = eqPos > -1 ? cookie.substring(0, eqPos) : cookie;
|
||||
document.cookie = name + "=;expires=Thu, 01 Jan 1970 00:00:00 GMT";
|
||||
});
|
||||
|
||||
notifications.success({
|
||||
title: "All Data Cleared",
|
||||
description: "All locally stored data has been removed",
|
||||
timeout: 3000
|
||||
});
|
||||
} catch (error) {
|
||||
notifications.error({
|
||||
title: "Error",
|
||||
description: "Failed to clear all data",
|
||||
timeout: 5000
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.storage-manager {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user