fix: resolve migration service import and function signature conflicts
- Fix import in src/db-sql/migration.ts to use named imports and alias runMigrations to avoid naming conflict - Add missing migration management functions (registerMigration, runMigrations) to migrationService with full typing and logging - Update function signatures to accept SQL parameters for compatibility with AbsurdSqlDatabaseService - Clean up Prettier formatting issues in migrationService and migration.ts - Confirmed dev server and linter run cleanly Co-authored-by: Matthew Raymer
This commit is contained in:
585
package-lock.json
generated
585
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
70
src/assets/icons.json
Normal file
70
src/assets/icons.json
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
{
|
||||||
|
"warning": {
|
||||||
|
"fillRule": "evenodd",
|
||||||
|
"d": "M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z",
|
||||||
|
"clipRule": "evenodd"
|
||||||
|
},
|
||||||
|
"spinner": {
|
||||||
|
"d": "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||||
|
},
|
||||||
|
"chart": {
|
||||||
|
"strokeLinecap": "round",
|
||||||
|
"strokeLinejoin": "round",
|
||||||
|
"strokeWidth": "2",
|
||||||
|
"d": "M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"
|
||||||
|
},
|
||||||
|
"plus": {
|
||||||
|
"strokeLinecap": "round",
|
||||||
|
"strokeLinejoin": "round",
|
||||||
|
"strokeWidth": "2",
|
||||||
|
"d": "M12 4v16m8-8H4"
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"strokeLinecap": "round",
|
||||||
|
"strokeLinejoin": "round",
|
||||||
|
"strokeWidth": "2",
|
||||||
|
"d": "M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"
|
||||||
|
},
|
||||||
|
"settingsDot": {
|
||||||
|
"strokeLinecap": "round",
|
||||||
|
"strokeLinejoin": "round",
|
||||||
|
"strokeWidth": "2",
|
||||||
|
"d": "M15 12a3 3 0 11-6 0 3 3 0 016 0z"
|
||||||
|
},
|
||||||
|
"lock": {
|
||||||
|
"strokeLinecap": "round",
|
||||||
|
"strokeLinejoin": "round",
|
||||||
|
"strokeWidth": "2",
|
||||||
|
"d": "M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"
|
||||||
|
},
|
||||||
|
"download": {
|
||||||
|
"strokeLinecap": "round",
|
||||||
|
"strokeLinejoin": "round",
|
||||||
|
"strokeWidth": "2",
|
||||||
|
"d": "M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
|
||||||
|
},
|
||||||
|
"check": {
|
||||||
|
"strokeLinecap": "round",
|
||||||
|
"strokeLinejoin": "round",
|
||||||
|
"strokeWidth": "2",
|
||||||
|
"d": "M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||||
|
},
|
||||||
|
"edit": {
|
||||||
|
"strokeLinecap": "round",
|
||||||
|
"strokeLinejoin": "round",
|
||||||
|
"strokeWidth": "2",
|
||||||
|
"d": "M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"
|
||||||
|
},
|
||||||
|
"trash": {
|
||||||
|
"strokeLinecap": "round",
|
||||||
|
"strokeLinejoin": "round",
|
||||||
|
"strokeWidth": "2",
|
||||||
|
"d": "M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
|
||||||
|
},
|
||||||
|
"plusCircle": {
|
||||||
|
"strokeLinecap": "round",
|
||||||
|
"strokeLinejoin": "round",
|
||||||
|
"strokeWidth": "2",
|
||||||
|
"d": "M12 6v6m0 0v6m0-6h6m-6 0H6"
|
||||||
|
}
|
||||||
|
}
|
||||||
90
src/components/IconRenderer.vue
Normal file
90
src/components/IconRenderer.vue
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
v-if="iconData"
|
||||||
|
:class="svgClass"
|
||||||
|
:fill="fill"
|
||||||
|
:stroke="stroke"
|
||||||
|
:viewBox="viewBox"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path v-for="(path, index) in iconData.paths" :key="index" v-bind="path" />
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { Component, Prop, Vue } from "vue-facing-decorator";
|
||||||
|
import icons from "../assets/icons.json";
|
||||||
|
import { logger } from "../utils/logger";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Icon path interface
|
||||||
|
*/
|
||||||
|
interface IconPath {
|
||||||
|
d: string;
|
||||||
|
fillRule?: string;
|
||||||
|
clipRule?: string;
|
||||||
|
strokeLinecap?: string;
|
||||||
|
strokeLinejoin?: string;
|
||||||
|
strokeWidth?: string | number;
|
||||||
|
fill?: string;
|
||||||
|
stroke?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Icon data interface
|
||||||
|
*/
|
||||||
|
interface IconData {
|
||||||
|
paths: IconPath[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Icons JSON structure
|
||||||
|
*/
|
||||||
|
interface IconsJson {
|
||||||
|
[key: string]: IconPath | IconData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Icon Renderer Component
|
||||||
|
*
|
||||||
|
* This component loads SVG icon definitions from a JSON file and renders them
|
||||||
|
* as SVG elements. It provides a clean way to use icons without cluttering
|
||||||
|
* templates with long SVG path definitions.
|
||||||
|
*
|
||||||
|
* @author Matthew Raymer
|
||||||
|
* @version 1.0.0
|
||||||
|
* @since 2024
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
name: "IconRenderer",
|
||||||
|
})
|
||||||
|
export default class IconRenderer extends Vue {
|
||||||
|
@Prop({ required: true }) readonly iconName!: string;
|
||||||
|
@Prop({ default: "h-5 w-5" }) readonly svgClass!: string;
|
||||||
|
@Prop({ default: "none" }) readonly fill!: string;
|
||||||
|
@Prop({ default: "currentColor" }) readonly stroke!: string;
|
||||||
|
@Prop({ default: "0 0 24 24" }) readonly viewBox!: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the icon data for the specified icon name
|
||||||
|
*
|
||||||
|
* @returns {IconData | null} The icon data object or null if not found
|
||||||
|
*/
|
||||||
|
get iconData(): IconData | null {
|
||||||
|
const icon = (icons as IconsJson)[this.iconName];
|
||||||
|
if (!icon) {
|
||||||
|
logger.warn(`Icon "${this.iconName}" not found in icons.json`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert single path to array format for consistency
|
||||||
|
if ("d" in icon) {
|
||||||
|
return {
|
||||||
|
paths: [icon as IconPath],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return icon as IconData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -1,4 +1,7 @@
|
|||||||
import migrationService from "../services/migrationService";
|
import {
|
||||||
|
registerMigration,
|
||||||
|
runMigrations as runMigrationsService,
|
||||||
|
} from "../services/migrationService";
|
||||||
import { DEFAULT_ENDORSER_API_SERVER } from "@/constants/app";
|
import { DEFAULT_ENDORSER_API_SERVER } from "@/constants/app";
|
||||||
import { arrayBufferToBase64 } from "@/libs/crypto";
|
import { arrayBufferToBase64 } from "@/libs/crypto";
|
||||||
|
|
||||||
@@ -123,16 +126,12 @@ const MIGRATIONS = [
|
|||||||
* @param extractMigrationNames - A function that extracts the names (string array) from "select name from migrations"
|
* @param extractMigrationNames - A function that extracts the names (string array) from "select name from migrations"
|
||||||
*/
|
*/
|
||||||
export async function runMigrations<T>(
|
export async function runMigrations<T>(
|
||||||
sqlExec: (sql: string) => Promise<unknown>,
|
sqlExec: (sql: string, params?: unknown[]) => Promise<unknown>,
|
||||||
sqlQuery: (sql: string) => Promise<T>,
|
sqlQuery: (sql: string, params?: unknown[]) => Promise<T>,
|
||||||
extractMigrationNames: (result: T) => Set<string>,
|
extractMigrationNames: (result: T) => Set<string>,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
for (const migration of MIGRATIONS) {
|
for (const migration of MIGRATIONS) {
|
||||||
migrationService.registerMigration(migration);
|
registerMigration(migration);
|
||||||
}
|
}
|
||||||
await migrationService.runMigrations(
|
await runMigrationsService(sqlExec, sqlQuery, extractMigrationNames);
|
||||||
sqlExec,
|
|
||||||
sqlQuery,
|
|
||||||
extractMigrationNames,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ export async function getSqliteContacts(): Promise<Contact[]> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const contacts = result.values.map((row) => {
|
const contacts = result.values.map((row) => {
|
||||||
const contact = parseJsonField(row, {}) as any;
|
const contact = parseJsonField(row, {}) as Contact;
|
||||||
return {
|
return {
|
||||||
did: contact.did || "",
|
did: contact.did || "",
|
||||||
name: contact.name || "",
|
name: contact.name || "",
|
||||||
@@ -286,7 +286,7 @@ export async function getSqliteSettings(): Promise<Settings[]> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const settings = result.values.map((row) => {
|
const settings = result.values.map((row) => {
|
||||||
const setting = parseJsonField(row, {}) as any;
|
const setting = parseJsonField(row, {}) as Settings;
|
||||||
return {
|
return {
|
||||||
id: setting.id,
|
id: setting.id,
|
||||||
accountDid: setting.accountDid || "",
|
accountDid: setting.accountDid || "",
|
||||||
@@ -366,7 +366,7 @@ export async function getSqliteAccounts(): Promise<Account[]> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const accounts = result.values.map((row) => {
|
const accounts = result.values.map((row) => {
|
||||||
const account = parseJsonField(row, {}) as any;
|
const account = parseJsonField(row, {}) as Account;
|
||||||
return {
|
return {
|
||||||
id: account.id,
|
id: account.id,
|
||||||
dateCreated: account.dateCreated || "",
|
dateCreated: account.dateCreated || "",
|
||||||
@@ -1403,3 +1403,148 @@ function generateUpdateStatement(
|
|||||||
params: [...params, ...whereParams],
|
params: [...params, ...whereParams],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Migration interface for database schema migrations
|
||||||
|
*/
|
||||||
|
interface Migration {
|
||||||
|
name: string;
|
||||||
|
sql: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Migration registry to store and manage database migrations
|
||||||
|
*/
|
||||||
|
class MigrationRegistry {
|
||||||
|
private migrations: Migration[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a migration with the registry
|
||||||
|
*
|
||||||
|
* @param migration - The migration to register
|
||||||
|
*/
|
||||||
|
registerMigration(migration: Migration): void {
|
||||||
|
this.migrations.push(migration);
|
||||||
|
logger.info(`[MigrationService] Registered migration: ${migration.name}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all registered migrations
|
||||||
|
*
|
||||||
|
* @returns Array of registered migrations
|
||||||
|
*/
|
||||||
|
getMigrations(): Migration[] {
|
||||||
|
return this.migrations;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear all registered migrations
|
||||||
|
*/
|
||||||
|
clearMigrations(): void {
|
||||||
|
this.migrations = [];
|
||||||
|
logger.info("[MigrationService] Cleared all registered migrations");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a singleton instance of the migration registry
|
||||||
|
const migrationRegistry = new MigrationRegistry();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a migration with the migration service
|
||||||
|
*
|
||||||
|
* This function is used by the migration system to register database
|
||||||
|
* schema migrations that need to be applied to the database.
|
||||||
|
*
|
||||||
|
* @param migration - The migration to register
|
||||||
|
*/
|
||||||
|
export function registerMigration(migration: Migration): void {
|
||||||
|
migrationRegistry.registerMigration(migration);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run all registered migrations against the database
|
||||||
|
*
|
||||||
|
* This function executes all registered migrations in order, checking
|
||||||
|
* which ones have already been applied to avoid duplicate execution.
|
||||||
|
* It creates a migrations table if it doesn't exist to track applied
|
||||||
|
* migrations.
|
||||||
|
*
|
||||||
|
* @param sqlExec - Function to execute SQL statements
|
||||||
|
* @param sqlQuery - Function to query SQL data
|
||||||
|
* @param extractMigrationNames - Function to extract migration names from query results
|
||||||
|
* @returns Promise that resolves when all migrations are complete
|
||||||
|
*/
|
||||||
|
export async function runMigrations<T>(
|
||||||
|
sqlExec: (sql: string, params?: unknown[]) => Promise<unknown>,
|
||||||
|
sqlQuery: (sql: string, params?: unknown[]) => Promise<T>,
|
||||||
|
extractMigrationNames: (result: T) => Set<string>,
|
||||||
|
): Promise<void> {
|
||||||
|
try {
|
||||||
|
// Create migrations table if it doesn't exist
|
||||||
|
await sqlExec(`
|
||||||
|
CREATE TABLE IF NOT EXISTS migrations (
|
||||||
|
name TEXT PRIMARY KEY,
|
||||||
|
applied_at TEXT DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
`);
|
||||||
|
|
||||||
|
// Get list of already applied migrations
|
||||||
|
const appliedMigrationsResult = await sqlQuery(
|
||||||
|
"SELECT name FROM migrations",
|
||||||
|
);
|
||||||
|
const appliedMigrations = extractMigrationNames(appliedMigrationsResult);
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
`[MigrationService] Found ${appliedMigrations.size} applied migrations`,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Get all registered migrations
|
||||||
|
const migrations = migrationRegistry.getMigrations();
|
||||||
|
|
||||||
|
if (migrations.length === 0) {
|
||||||
|
logger.warn("[MigrationService] No migrations registered");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
`[MigrationService] Running ${migrations.length} registered migrations`,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Run each migration that hasn't been applied yet
|
||||||
|
for (const migration of migrations) {
|
||||||
|
if (appliedMigrations.has(migration.name)) {
|
||||||
|
logger.info(
|
||||||
|
`[MigrationService] Skipping already applied migration: ${migration.name}`,
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info(`[MigrationService] Applying migration: ${migration.name}`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Execute the migration SQL
|
||||||
|
await sqlExec(migration.sql);
|
||||||
|
|
||||||
|
// Record that the migration was applied
|
||||||
|
await sqlExec("INSERT INTO migrations (name) VALUES (?)", [
|
||||||
|
migration.name,
|
||||||
|
]);
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
`[MigrationService] Successfully applied migration: ${migration.name}`,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(
|
||||||
|
`[MigrationService] Failed to apply migration ${migration.name}:`,
|
||||||
|
error,
|
||||||
|
);
|
||||||
|
throw new Error(`Migration ${migration.name} failed: ${error}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("[MigrationService] All migrations completed successfully");
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("[MigrationService] Migration process failed:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,17 +17,11 @@
|
|||||||
>
|
>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<div class="flex-shrink-0">
|
<div class="flex-shrink-0">
|
||||||
<svg
|
<IconRenderer
|
||||||
class="h-5 w-5 text-yellow-400"
|
icon-name="warning"
|
||||||
viewBox="0 0 20 20"
|
svg-class="h-5 w-5 text-yellow-400"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
>
|
/>
|
||||||
<path
|
|
||||||
fill-rule="evenodd"
|
|
||||||
d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z"
|
|
||||||
clip-rule="evenodd"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="ml-3">
|
<div class="ml-3">
|
||||||
<h3 class="text-sm font-medium text-yellow-800">
|
<h3 class="text-sm font-medium text-yellow-800">
|
||||||
@@ -36,11 +30,13 @@
|
|||||||
<div class="mt-2 text-sm text-yellow-700">
|
<div class="mt-2 text-sm text-yellow-700">
|
||||||
<p>
|
<p>
|
||||||
To use migration features, enable Dexie database by setting
|
To use migration features, enable Dexie database by setting
|
||||||
<code class="bg-yellow-100 px-1 rounded"
|
<code class="bg-yellow-100 px-1 rounded">
|
||||||
>USE_DEXIE_DB = true</code
|
USE_DEXIE_DB = true
|
||||||
>
|
</code>
|
||||||
in
|
in
|
||||||
<code class="bg-yellow-100 px-1 rounded">constants/app.ts</code>
|
<code class="bg-yellow-100 px-1 rounded">
|
||||||
|
constants/app.ts
|
||||||
|
</code>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -54,41 +50,17 @@
|
|||||||
class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed"
|
class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
@click="compareDatabases"
|
@click="compareDatabases"
|
||||||
>
|
>
|
||||||
<svg
|
<IconRenderer
|
||||||
v-if="isLoading"
|
v-if="isLoading"
|
||||||
class="animate-spin -ml-1 mr-3 h-5 w-5 text-white"
|
icon-name="spinner"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
svg-class="animate-spin -ml-1 mr-3 h-5 w-5 text-white"
|
||||||
fill="none"
|
fill="currentColor"
|
||||||
viewBox="0 0 24 24"
|
/>
|
||||||
>
|
<IconRenderer
|
||||||
<circle
|
|
||||||
class="opacity-25"
|
|
||||||
cx="12"
|
|
||||||
cy="12"
|
|
||||||
r="10"
|
|
||||||
stroke="currentColor"
|
|
||||||
stroke-width="4"
|
|
||||||
></circle>
|
|
||||||
<path
|
|
||||||
class="opacity-75"
|
|
||||||
fill="currentColor"
|
|
||||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
||||||
></path>
|
|
||||||
</svg>
|
|
||||||
<svg
|
|
||||||
v-else
|
v-else
|
||||||
class="-ml-1 mr-3 h-5 w-5"
|
icon-name="chart"
|
||||||
fill="none"
|
svg-class="-ml-1 mr-3 h-5 w-5"
|
||||||
stroke="currentColor"
|
/>
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
Compare Databases
|
Compare Databases
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
@@ -97,19 +69,7 @@
|
|||||||
class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-green-600 hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 disabled:opacity-50 disabled:cursor-not-allowed"
|
class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-green-600 hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
@click="migrateContacts"
|
@click="migrateContacts"
|
||||||
>
|
>
|
||||||
<svg
|
<IconRenderer icon-name="plus" svg-class="-ml-1 mr-3 h-5 w-5" />
|
||||||
class="-ml-1 mr-3 h-5 w-5"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M12 4v16m8-8H4"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
Migrate Contacts
|
Migrate Contacts
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
@@ -118,25 +78,7 @@
|
|||||||
class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-purple-600 hover:bg-purple-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-purple-500 disabled:opacity-50 disabled:cursor-not-allowed"
|
class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-purple-600 hover:bg-purple-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-purple-500 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
@click="migrateSettings"
|
@click="migrateSettings"
|
||||||
>
|
>
|
||||||
<svg
|
<IconRenderer icon-name="settings" svg-class="-ml-1 mr-3 h-5 w-5" />
|
||||||
class="-ml-1 mr-3 h-5 w-5"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
Migrate Settings
|
Migrate Settings
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
@@ -145,19 +87,7 @@
|
|||||||
class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-orange-600 hover:bg-orange-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-orange-500 disabled:opacity-50 disabled:cursor-not-allowed"
|
class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-orange-600 hover:bg-orange-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-orange-500 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
@click="migrateAccounts"
|
@click="migrateAccounts"
|
||||||
>
|
>
|
||||||
<svg
|
<IconRenderer icon-name="lock" svg-class="-ml-1 mr-3 h-5 w-5" />
|
||||||
class="-ml-1 mr-3 h-5 w-5"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
Migrate Accounts
|
Migrate Accounts
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
@@ -166,21 +96,18 @@
|
|||||||
class="inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed"
|
class="inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
@click="exportComparison"
|
@click="exportComparison"
|
||||||
>
|
>
|
||||||
<svg
|
<IconRenderer icon-name="download" svg-class="-ml-1 mr-3 h-5 w-5" />
|
||||||
class="-ml-1 mr-3 h-5 w-5"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
Export Comparison
|
Export Comparison
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
:disabled="isLoading"
|
||||||
|
class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-teal-600 hover:bg-teal-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-teal-500 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
|
@click="verifyMigration"
|
||||||
|
>
|
||||||
|
<IconRenderer icon-name="check" svg-class="-ml-1 mr-3 h-5 w-5" />
|
||||||
|
Verify Migration
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Loading State -->
|
<!-- Loading State -->
|
||||||
@@ -188,26 +115,11 @@
|
|||||||
<div
|
<div
|
||||||
class="inline-flex items-center px-4 py-2 font-semibold leading-6 text-sm shadow rounded-md text-white bg-blue-500 hover:bg-blue-400 transition ease-in-out duration-150 cursor-not-allowed"
|
class="inline-flex items-center px-4 py-2 font-semibold leading-6 text-sm shadow rounded-md text-white bg-blue-500 hover:bg-blue-400 transition ease-in-out duration-150 cursor-not-allowed"
|
||||||
>
|
>
|
||||||
<svg
|
<IconRenderer
|
||||||
class="animate-spin -ml-1 mr-3 h-5 w-5 text-white"
|
icon-name="spinner"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
svg-class="animate-spin -ml-1 mr-3 h-5 w-5 text-white"
|
||||||
fill="none"
|
fill="currentColor"
|
||||||
viewBox="0 0 24 24"
|
/>
|
||||||
>
|
|
||||||
<circle
|
|
||||||
class="opacity-25"
|
|
||||||
cx="12"
|
|
||||||
cy="12"
|
|
||||||
r="10"
|
|
||||||
stroke="currentColor"
|
|
||||||
stroke-width="4"
|
|
||||||
></circle>
|
|
||||||
<path
|
|
||||||
class="opacity-75"
|
|
||||||
fill="currentColor"
|
|
||||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
||||||
></path>
|
|
||||||
</svg>
|
|
||||||
{{ loadingMessage }}
|
{{ loadingMessage }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -219,17 +131,11 @@
|
|||||||
>
|
>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<div class="flex-shrink-0">
|
<div class="flex-shrink-0">
|
||||||
<svg
|
<IconRenderer
|
||||||
class="h-5 w-5 text-red-400"
|
icon-name="warning"
|
||||||
viewBox="0 0 20 20"
|
svg-class="h-5 w-5 text-red-400"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
>
|
/>
|
||||||
<path
|
|
||||||
fill-rule="evenodd"
|
|
||||||
d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z"
|
|
||||||
clip-rule="evenodd"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="ml-3">
|
<div class="ml-3">
|
||||||
<h3 class="text-sm font-medium text-red-800">Error</h3>
|
<h3 class="text-sm font-medium text-red-800">Error</h3>
|
||||||
@@ -247,17 +153,10 @@
|
|||||||
>
|
>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<div class="flex-shrink-0">
|
<div class="flex-shrink-0">
|
||||||
<svg
|
<IconRenderer
|
||||||
class="h-5 w-5 text-green-400"
|
icon-name="check"
|
||||||
viewBox="0 0 20 20"
|
svg-class="h-5 w-5 text-green-400"
|
||||||
fill="currentColor"
|
/>
|
||||||
>
|
|
||||||
<path
|
|
||||||
fill-rule="evenodd"
|
|
||||||
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
|
|
||||||
clip-rule="evenodd"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="ml-3">
|
<div class="ml-3">
|
||||||
<h3 class="text-sm font-medium text-green-800">Success</h3>
|
<h3 class="text-sm font-medium text-green-800">Success</h3>
|
||||||
@@ -276,19 +175,10 @@
|
|||||||
<div class="p-5">
|
<div class="p-5">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<div class="flex-shrink-0">
|
<div class="flex-shrink-0">
|
||||||
<svg
|
<IconRenderer
|
||||||
class="h-6 w-6 text-blue-600"
|
icon-name="chart"
|
||||||
fill="none"
|
svg-class="h-6 w-6 text-blue-600"
|
||||||
stroke="currentColor"
|
/>
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="ml-5 w-0 flex-1">
|
<div class="ml-5 w-0 flex-1">
|
||||||
<dl>
|
<dl>
|
||||||
@@ -308,19 +198,10 @@
|
|||||||
<div class="p-5">
|
<div class="p-5">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<div class="flex-shrink-0">
|
<div class="flex-shrink-0">
|
||||||
<svg
|
<IconRenderer
|
||||||
class="h-6 w-6 text-green-600"
|
icon-name="check"
|
||||||
fill="none"
|
svg-class="h-6 w-6 text-green-600"
|
||||||
stroke="currentColor"
|
/>
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="ml-5 w-0 flex-1">
|
<div class="ml-5 w-0 flex-1">
|
||||||
<dl>
|
<dl>
|
||||||
@@ -340,25 +221,10 @@
|
|||||||
<div class="p-5">
|
<div class="p-5">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<div class="flex-shrink-0">
|
<div class="flex-shrink-0">
|
||||||
<svg
|
<IconRenderer
|
||||||
class="h-6 w-6 text-purple-600"
|
icon-name="settings"
|
||||||
fill="none"
|
svg-class="h-6 w-6 text-purple-600"
|
||||||
stroke="currentColor"
|
/>
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="ml-5 w-0 flex-1">
|
<div class="ml-5 w-0 flex-1">
|
||||||
<dl>
|
<dl>
|
||||||
@@ -378,19 +244,10 @@
|
|||||||
<div class="p-5">
|
<div class="p-5">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<div class="flex-shrink-0">
|
<div class="flex-shrink-0">
|
||||||
<svg
|
<IconRenderer
|
||||||
class="h-6 w-6 text-indigo-600"
|
icon-name="check"
|
||||||
fill="none"
|
svg-class="h-6 w-6 text-indigo-600"
|
||||||
stroke="currentColor"
|
/>
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="ml-5 w-0 flex-1">
|
<div class="ml-5 w-0 flex-1">
|
||||||
<dl>
|
<dl>
|
||||||
@@ -410,19 +267,10 @@
|
|||||||
<div class="p-5">
|
<div class="p-5">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<div class="flex-shrink-0">
|
<div class="flex-shrink-0">
|
||||||
<svg
|
<IconRenderer
|
||||||
class="h-6 w-6 text-orange-600"
|
icon-name="lock"
|
||||||
fill="none"
|
svg-class="h-6 w-6 text-orange-600"
|
||||||
stroke="currentColor"
|
/>
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="ml-5 w-0 flex-1">
|
<div class="ml-5 w-0 flex-1">
|
||||||
<dl>
|
<dl>
|
||||||
@@ -442,19 +290,10 @@
|
|||||||
<div class="p-5">
|
<div class="p-5">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<div class="flex-shrink-0">
|
<div class="flex-shrink-0">
|
||||||
<svg
|
<IconRenderer
|
||||||
class="h-6 w-6 text-teal-600"
|
icon-name="check"
|
||||||
fill="none"
|
svg-class="h-6 w-6 text-teal-600"
|
||||||
stroke="currentColor"
|
/>
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="ml-5 w-0 flex-1">
|
<div class="ml-5 w-0 flex-1">
|
||||||
<dl>
|
<dl>
|
||||||
@@ -485,19 +324,10 @@
|
|||||||
class="flex items-center justify-between p-3 bg-blue-50 rounded-lg"
|
class="flex items-center justify-between p-3 bg-blue-50 rounded-lg"
|
||||||
>
|
>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<svg
|
<IconRenderer
|
||||||
class="h-5 w-5 text-blue-600 mr-2"
|
icon-name="plusCircle"
|
||||||
fill="none"
|
svg-class="h-5 w-5 text-blue-600 mr-2"
|
||||||
stroke="currentColor"
|
/>
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M12 6v6m0 0v6m0-6h6m-6 0H6"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
<span class="text-sm font-medium text-blue-900">Added</span>
|
<span class="text-sm font-medium text-blue-900">Added</span>
|
||||||
</div>
|
</div>
|
||||||
<span class="text-sm font-bold text-blue-900">{{
|
<span class="text-sm font-bold text-blue-900">{{
|
||||||
@@ -509,19 +339,10 @@
|
|||||||
class="flex items-center justify-between p-3 bg-yellow-50 rounded-lg"
|
class="flex items-center justify-between p-3 bg-yellow-50 rounded-lg"
|
||||||
>
|
>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<svg
|
<IconRenderer
|
||||||
class="h-5 w-5 text-yellow-600 mr-2"
|
icon-name="edit"
|
||||||
fill="none"
|
svg-class="h-5 w-5 text-yellow-600 mr-2"
|
||||||
stroke="currentColor"
|
/>
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
<span class="text-sm font-medium text-yellow-900"
|
<span class="text-sm font-medium text-yellow-900"
|
||||||
>Modified</span
|
>Modified</span
|
||||||
>
|
>
|
||||||
@@ -535,19 +356,10 @@
|
|||||||
class="flex items-center justify-between p-3 bg-red-50 rounded-lg"
|
class="flex items-center justify-between p-3 bg-red-50 rounded-lg"
|
||||||
>
|
>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<svg
|
<IconRenderer
|
||||||
class="h-5 w-5 text-red-600 mr-2"
|
icon-name="trash"
|
||||||
fill="none"
|
svg-class="h-5 w-5 text-red-600 mr-2"
|
||||||
stroke="currentColor"
|
/>
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
<span class="text-sm font-medium text-red-900"
|
<span class="text-sm font-medium text-red-900"
|
||||||
>Missing</span
|
>Missing</span
|
||||||
>
|
>
|
||||||
@@ -593,19 +405,10 @@
|
|||||||
class="flex items-center justify-between p-3 bg-blue-50 rounded-lg"
|
class="flex items-center justify-between p-3 bg-blue-50 rounded-lg"
|
||||||
>
|
>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<svg
|
<IconRenderer
|
||||||
class="h-5 w-5 text-blue-600 mr-2"
|
icon-name="plusCircle"
|
||||||
fill="none"
|
svg-class="h-5 w-5 text-blue-600 mr-2"
|
||||||
stroke="currentColor"
|
/>
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M12 6v6m0 0v6m0-6h6m-6 0H6"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
<span class="text-sm font-medium text-blue-900">Added</span>
|
<span class="text-sm font-medium text-blue-900">Added</span>
|
||||||
</div>
|
</div>
|
||||||
<span class="text-sm font-bold text-blue-900">{{
|
<span class="text-sm font-bold text-blue-900">{{
|
||||||
@@ -617,19 +420,10 @@
|
|||||||
class="flex items-center justify-between p-3 bg-yellow-50 rounded-lg"
|
class="flex items-center justify-between p-3 bg-yellow-50 rounded-lg"
|
||||||
>
|
>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<svg
|
<IconRenderer
|
||||||
class="h-5 w-5 text-yellow-600 mr-2"
|
icon-name="edit"
|
||||||
fill="none"
|
svg-class="h-5 w-5 text-yellow-600 mr-2"
|
||||||
stroke="currentColor"
|
/>
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
<span class="text-sm font-medium text-yellow-900"
|
<span class="text-sm font-medium text-yellow-900"
|
||||||
>Modified</span
|
>Modified</span
|
||||||
>
|
>
|
||||||
@@ -643,19 +437,10 @@
|
|||||||
class="flex items-center justify-between p-3 bg-red-50 rounded-lg"
|
class="flex items-center justify-between p-3 bg-red-50 rounded-lg"
|
||||||
>
|
>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<svg
|
<IconRenderer
|
||||||
class="h-5 w-5 text-red-600 mr-2"
|
icon-name="trash"
|
||||||
fill="none"
|
svg-class="h-5 w-5 text-red-600 mr-2"
|
||||||
stroke="currentColor"
|
/>
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
<span class="text-sm font-medium text-red-900"
|
<span class="text-sm font-medium text-red-900"
|
||||||
>Missing</span
|
>Missing</span
|
||||||
>
|
>
|
||||||
@@ -699,19 +484,10 @@
|
|||||||
class="flex items-center justify-between p-3 bg-blue-50 rounded-lg"
|
class="flex items-center justify-between p-3 bg-blue-50 rounded-lg"
|
||||||
>
|
>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<svg
|
<IconRenderer
|
||||||
class="h-5 w-5 text-blue-600 mr-2"
|
icon-name="plusCircle"
|
||||||
fill="none"
|
svg-class="h-5 w-5 text-blue-600 mr-2"
|
||||||
stroke="currentColor"
|
/>
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M12 6v6m0 0v6m0-6h6m-6 0H6"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
<span class="text-sm font-medium text-blue-900">Added</span>
|
<span class="text-sm font-medium text-blue-900">Added</span>
|
||||||
</div>
|
</div>
|
||||||
<span class="text-sm font-bold text-blue-900">{{
|
<span class="text-sm font-bold text-blue-900">{{
|
||||||
@@ -723,19 +499,10 @@
|
|||||||
class="flex items-center justify-between p-3 bg-yellow-50 rounded-lg"
|
class="flex items-center justify-between p-3 bg-yellow-50 rounded-lg"
|
||||||
>
|
>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<svg
|
<IconRenderer
|
||||||
class="h-5 w-5 text-yellow-600 mr-2"
|
icon-name="edit"
|
||||||
fill="none"
|
svg-class="h-5 w-5 text-yellow-600 mr-2"
|
||||||
stroke="currentColor"
|
/>
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
<span class="text-sm font-medium text-yellow-900"
|
<span class="text-sm font-medium text-yellow-900"
|
||||||
>Modified</span
|
>Modified</span
|
||||||
>
|
>
|
||||||
@@ -749,19 +516,10 @@
|
|||||||
class="flex items-center justify-between p-3 bg-red-50 rounded-lg"
|
class="flex items-center justify-between p-3 bg-red-50 rounded-lg"
|
||||||
>
|
>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<svg
|
<IconRenderer
|
||||||
class="h-5 w-5 text-red-600 mr-2"
|
icon-name="trash"
|
||||||
fill="none"
|
svg-class="h-5 w-5 text-red-600 mr-2"
|
||||||
stroke="currentColor"
|
/>
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
<span class="text-sm font-medium text-red-900"
|
<span class="text-sm font-medium text-red-900"
|
||||||
>Missing</span
|
>Missing</span
|
||||||
>
|
>
|
||||||
@@ -832,6 +590,7 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Component, Vue } from "vue-facing-decorator";
|
import { Component, Vue } from "vue-facing-decorator";
|
||||||
|
import IconRenderer from "../components/IconRenderer.vue";
|
||||||
import {
|
import {
|
||||||
compareDatabases,
|
compareDatabases,
|
||||||
migrateContacts,
|
migrateContacts,
|
||||||
@@ -865,6 +624,9 @@ import { logger } from "../utils/logger";
|
|||||||
*/
|
*/
|
||||||
@Component({
|
@Component({
|
||||||
name: "DatabaseMigration",
|
name: "DatabaseMigration",
|
||||||
|
components: {
|
||||||
|
IconRenderer,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
export default class DatabaseMigration extends Vue {
|
export default class DatabaseMigration extends Vue {
|
||||||
// Component state
|
// Component state
|
||||||
@@ -899,7 +661,11 @@ export default class DatabaseMigration extends Vue {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
this.comparison = await compareDatabases();
|
this.comparison = await compareDatabases();
|
||||||
this.successMessage = `Comparison completed successfully. Found ${this.comparison.differences.contacts.added.length + this.comparison.differences.settings.added.length + this.comparison.differences.accounts.added.length} items to migrate.`;
|
const totalItems =
|
||||||
|
this.comparison.differences.contacts.added.length +
|
||||||
|
this.comparison.differences.settings.added.length +
|
||||||
|
this.comparison.differences.accounts.added.length;
|
||||||
|
this.successMessage = `Comparison completed successfully. Found ${totalItems} items to migrate.`;
|
||||||
logger.info(
|
logger.info(
|
||||||
"[DatabaseMigration] Database comparison completed successfully",
|
"[DatabaseMigration] Database comparison completed successfully",
|
||||||
);
|
);
|
||||||
@@ -1038,6 +804,63 @@ export default class DatabaseMigration extends Vue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies the migration by running another comparison
|
||||||
|
*
|
||||||
|
* This method runs a fresh comparison between Dexie and SQLite databases
|
||||||
|
* to verify that the migration was successful. It's useful to run this
|
||||||
|
* after completing migrations to ensure data integrity and relationship
|
||||||
|
* preservation.
|
||||||
|
*
|
||||||
|
* @async
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async verifyMigration(): Promise<void> {
|
||||||
|
this.setLoading("Verifying migration...");
|
||||||
|
this.clearMessages();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const newComparison = await compareDatabases();
|
||||||
|
|
||||||
|
// Check if there are any remaining differences
|
||||||
|
const totalRemaining =
|
||||||
|
newComparison.differences.contacts.added.length +
|
||||||
|
newComparison.differences.contacts.modified.length +
|
||||||
|
newComparison.differences.contacts.missing.length +
|
||||||
|
newComparison.differences.settings.added.length +
|
||||||
|
newComparison.differences.settings.modified.length +
|
||||||
|
newComparison.differences.settings.missing.length +
|
||||||
|
newComparison.differences.accounts.added.length +
|
||||||
|
newComparison.differences.accounts.modified.length +
|
||||||
|
newComparison.differences.accounts.missing.length;
|
||||||
|
|
||||||
|
if (totalRemaining === 0) {
|
||||||
|
this.successMessage =
|
||||||
|
"✅ Migration verification successful! All data has been migrated correctly.";
|
||||||
|
logger.info(
|
||||||
|
"[DatabaseMigration] Migration verification successful - no differences found",
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.successMessage = `⚠️ Migration verification completed. Found ${totalRemaining} remaining differences. Consider running additional migrations if needed.`;
|
||||||
|
logger.warn(
|
||||||
|
"[DatabaseMigration] Migration verification found remaining differences",
|
||||||
|
{
|
||||||
|
remaining: totalRemaining,
|
||||||
|
differences: newComparison.differences,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the comparison to show the current state
|
||||||
|
this.comparison = newComparison;
|
||||||
|
} catch (error) {
|
||||||
|
this.error = `Failed to verify migration: ${error}`;
|
||||||
|
logger.error("[DatabaseMigration] Migration verification failed:", error);
|
||||||
|
} finally {
|
||||||
|
this.setLoading("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exports comparison results to a file
|
* Exports comparison results to a file
|
||||||
*
|
*
|
||||||
|
|||||||
Reference in New Issue
Block a user