diff --git a/listify-api/src/app.module.ts b/listify-api/src/app.module.ts index 91d38b6..2fcf3d4 100644 --- a/listify-api/src/app.module.ts +++ b/listify-api/src/app.module.ts @@ -43,7 +43,7 @@ import { DatabaseLogger } from './database/database.logger'; password: configService.get('DB_PASSWORD', ''), database: configService.get('DB_DATABASE', 'listify'), autoLoadEntities: true, - synchronize: false, + synchronize: true, logging: parseDatabaseLogging(env.DB_LOGGING), logger: new DatabaseLogger(databaseLoggerOptionsFromEnv(env)), maxQueryExecutionTime: slowQueryThresholdFromEnv(env), diff --git a/listify-api/src/database/migrations/1781093004780-GeneratedMigration.ts b/listify-api/src/database/migrations/1781093004780-GeneratedMigration.ts new file mode 100644 index 0000000..e10a924 --- /dev/null +++ b/listify-api/src/database/migrations/1781093004780-GeneratedMigration.ts @@ -0,0 +1,18 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class GeneratedMigration1781093004780 implements MigrationInterface { + name = 'GeneratedMigration1781093004780' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`CREATE TABLE \`audit_logs\` (\`id\` varchar(36) NOT NULL, \`actorUserId\` varchar(36) NULL, \`actorEmail\` varchar(320) NULL, \`action\` varchar(100) NOT NULL, \`entityType\` varchar(80) NULL, \`entityId\` varchar(36) NULL, \`metadata\` json NULL, \`ipAddress\` varchar(64) NULL, \`userAgent\` varchar(512) NULL, \`createdAt\` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), INDEX \`IDX_audit_logs_actor_user_id\` (\`actorUserId\`), INDEX \`IDX_audit_logs_action\` (\`action\`), INDEX \`IDX_audit_logs_entity\` (\`entityType\`), INDEX \`IDX_audit_logs_created_at\` (\`createdAt\`), PRIMARY KEY (\`id\`)) ENGINE=InnoDB`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DROP INDEX \`IDX_audit_logs_created_at\` ON \`audit_logs\``); + await queryRunner.query(`DROP INDEX \`IDX_audit_logs_entity\` ON \`audit_logs\``); + await queryRunner.query(`DROP INDEX \`IDX_audit_logs_action\` ON \`audit_logs\``); + await queryRunner.query(`DROP INDEX \`IDX_audit_logs_actor_user_id\` ON \`audit_logs\``); + await queryRunner.query(`DROP TABLE \`audit_logs\``); + } + +} diff --git a/listify-client/src/app/account/account.component.scss b/listify-client/src/app/account/account.component.scss index c7f3f8a..635383c 100644 --- a/listify-client/src/app/account/account.component.scss +++ b/listify-client/src/app/account/account.component.scss @@ -1,5 +1,5 @@ .account-page { - min-height: inherit; + min-height: calc(100dvh - 56px); display: grid; align-items: start; padding: 1rem; @@ -7,7 +7,11 @@ .account-card { width: min(100%, 520px); + overflow: hidden; + border: 1px solid color-mix(in srgb, var(--mat-sys-outline-variant) 72%, transparent); border-radius: 8px; + background: var(--mat-sys-surface); + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08); } .status-row { @@ -15,6 +19,10 @@ align-items: center; gap: 0.75rem; margin-top: 1rem; + padding: 0.75rem; + border: 1px solid color-mix(in srgb, var(--mat-sys-outline-variant) 72%, transparent); + border-radius: 8px; + background: color-mix(in srgb, var(--mat-sys-surface-container-low) 36%, var(--mat-sys-surface)); color: var(--mat-sys-on-surface-variant); } @@ -34,6 +42,7 @@ mat-card-actions mat-progress-spinner { @media (min-width: 600px) { .account-page { + min-height: calc(100dvh - 64px); place-items: center; padding: 2rem 1rem; } diff --git a/listify-client/src/app/app.scss b/listify-client/src/app/app.scss index cff21da..abe132c 100644 --- a/listify-client/src/app/app.scss +++ b/listify-client/src/app/app.scss @@ -2,9 +2,9 @@ display: block; min-height: 100dvh; background: - linear-gradient(135deg, rgba(20, 105, 84, 0.12), transparent 36%), - linear-gradient(315deg, rgba(123, 92, 40, 0.12), transparent 34%), - var(--mat-sys-surface); + linear-gradient(140deg, color-mix(in srgb, var(--mat-sys-primary) 14%, transparent), transparent 38%), + linear-gradient(320deg, color-mix(in srgb, var(--mat-sys-tertiary) 12%, transparent), transparent 36%), + var(--mat-sys-surface-container); } .app-toolbar { @@ -14,8 +14,8 @@ height: 56px; min-height: 56px; padding-inline: 0.75rem; - border-bottom: 1px solid var(--mat-sys-outline-variant); - background: color-mix(in srgb, var(--mat-sys-surface) 92%, transparent); + border-bottom: 1px solid color-mix(in srgb, var(--mat-sys-outline-variant) 72%, transparent); + background: color-mix(in srgb, var(--mat-sys-surface) 88%, transparent); color: var(--mat-sys-on-surface); backdrop-filter: blur(14px); } @@ -31,7 +31,15 @@ } .brand mat-icon { + display: grid; + place-items: center; + width: 32px; + height: 32px; + border: 1px solid color-mix(in srgb, var(--mat-sys-primary) 16%, transparent); + border-radius: 8px; + background: color-mix(in srgb, var(--mat-sys-primary) 10%, transparent); color: var(--mat-sys-primary); + font-size: 22px; } .menu-button { @@ -68,8 +76,9 @@ .sidebar { width: min(82vw, 280px); - border-right: 1px solid var(--mat-sys-outline-variant); - background: color-mix(in srgb, var(--mat-sys-surface-container-low) 96%, white); + border-right: 1px solid color-mix(in srgb, var(--mat-sys-outline-variant) 72%, transparent); + background: color-mix(in srgb, var(--mat-sys-surface) 92%, transparent); + backdrop-filter: blur(14px); } .sidebar nav { @@ -112,8 +121,8 @@ grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0.25rem; padding: 0.35rem 0.5rem calc(0.35rem + env(safe-area-inset-bottom)); - border-top: 1px solid var(--mat-sys-outline-variant); - background: color-mix(in srgb, var(--mat-sys-surface) 96%, transparent); + border-top: 1px solid color-mix(in srgb, var(--mat-sys-outline-variant) 72%, transparent); + background: color-mix(in srgb, var(--mat-sys-surface) 90%, transparent); backdrop-filter: blur(14px); } diff --git a/listify-client/src/app/auth/auth-page.scss b/listify-client/src/app/auth/auth-page.scss index b2c4cf3..6dacd3a 100644 --- a/listify-client/src/app/auth/auth-page.scss +++ b/listify-client/src/app/auth/auth-page.scss @@ -1,32 +1,99 @@ +:host { + display: block; + min-height: calc(100dvh - 56px); +} + .auth-page { - min-height: inherit; + min-height: calc(100dvh - 56px); display: grid; align-items: start; - padding: 1rem; + padding: 1.25rem; + background: + linear-gradient(140deg, color-mix(in srgb, var(--mat-sys-primary) 14%, transparent), transparent 38%), + linear-gradient(320deg, color-mix(in srgb, var(--mat-sys-tertiary) 12%, transparent), transparent 36%), + var(--mat-sys-surface-container); } .auth-card { width: min(100%, 440px); + overflow: hidden; + border: 1px solid color-mix(in srgb, var(--mat-sys-outline-variant) 72%, transparent); border-radius: 8px; - background: color-mix(in srgb, var(--mat-sys-surface-container-low) 94%, white); + background: var(--mat-sys-surface); + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08); + transition: box-shadow 0.3s ease, transform 0.3s ease; +} + +.auth-card:hover { + box-shadow: 0 8px 25px rgba(0, 0, 0, 0.12); } .auth-card mat-card-header { - padding-bottom: 0.75rem; + padding-bottom: 0.5rem; + text-align: center; +} + +.auth-card mat-card-header mat-card-title { + font-size: 1.8rem; + font-weight: 600; + color: var(--mat-sys-on-surface); + margin-bottom: 0.25rem; +} + +.auth-card mat-card-header mat-card-subtitle { + font-size: 0.95rem; + color: var(--mat-sys-on-surface-variant); +} + +.auth-logo { + display: grid; + place-items: center; + width: 64px; + height: 64px; + margin: 1.5rem auto 0.25rem; + border: 1px solid color-mix(in srgb, var(--mat-sys-primary) 16%, transparent); + border-radius: 8px; + background: + linear-gradient(145deg, color-mix(in srgb, var(--mat-sys-primary) 18%, transparent), transparent), + var(--mat-sys-surface-container-low); + color: var(--mat-sys-primary); +} + +.auth-logo mat-icon { + display: grid; + place-items: center; + width: 38px; + height: 38px; + font-size: 38px; + line-height: 1; } .auth-form { display: grid; - gap: 0.65rem; - padding-top: 0.75rem; + gap: 1rem; + padding-top: 1rem; } .auth-form mat-form-field { width: 100%; + transition: all 0.3s ease; +} + +.auth-form mat-form-field:focus-within { + mat-icon { + color: var(--mat-sys-primary); + } } .auth-form button[type='submit'] { min-height: 48px; + font-size: 1rem; + font-weight: 500; + border-radius: 8px; +} + +.auth-form button[type='submit']:disabled { + opacity: 0.7; } .auth-form mat-progress-spinner { @@ -36,7 +103,9 @@ .auth-card mat-card-actions { flex-wrap: wrap; - gap: 0.5rem; + gap: 0.75rem; + padding-top: 1.5rem; + justify-content: space-between; } .auth-card mat-card-actions mat-progress-spinner { @@ -44,6 +113,31 @@ margin-right: 0.5rem; } +.divider-container { + display: flex; + align-items: center; + gap: 1rem; + margin: 0.5rem 0; +} + +.divider-container::before, +.divider-container::after { + content: ''; + flex: 1; + height: 1px; + background: var(--mat-sys-outline-variant); +} + +.divider-text { + color: var(--mat-sys-on-surface-variant); + font-size: 0.875rem; + white-space: nowrap; +} + +.register-link { + margin-left: auto; +} + .verify-card mat-card-content { padding-top: 1rem; } @@ -86,3 +180,10 @@ gap: 0.75rem; } } + +@media (min-width: 801px) { + :host, + .auth-page { + min-height: calc(100dvh - 64px); + } +} diff --git a/listify-client/src/app/auth/login/login.component.html b/listify-client/src/app/auth/login/login.component.html index 212837c..13eefcf 100644 --- a/listify-client/src/app/auth/login/login.component.html +++ b/listify-client/src/app/auth/login/login.component.html @@ -1,8 +1,11 @@
- + + - Login - Mit deinem Listify-Konto anmelden + Willkommen zurueck + Melden Sie sich mit Ihrem Listify-Konto an @@ -12,9 +15,9 @@ @if (form.controls.email.hasError('required')) { - E-Mail ist erforderlich. + E-Mail ist erforderlich } @else if (form.controls.email.hasError('email')) { - Bitte gib eine gültige E-Mail ein. + Bitte geben Sie eine gueltige E-Mail ein } @@ -36,9 +39,9 @@ @if (form.controls.password.hasError('required')) { - Passwort ist erforderlich. + Passwort ist erforderlich } @else if (form.controls.password.hasError('minlength')) { - Mindestens 8 Zeichen. + Mindestens 8 Zeichen } @@ -50,24 +53,33 @@ } Einloggen + +
+ oder +
+ +
- - - Konto erstellen + + Neu hier? + + Konto erstellen +
diff --git a/listify-client/src/app/auth/register/register.component.html b/listify-client/src/app/auth/register/register.component.html index 1f654e4..495b5d1 100644 --- a/listify-client/src/app/auth/register/register.component.html +++ b/listify-client/src/app/auth/register/register.component.html @@ -1,5 +1,8 @@
+ Registrieren Neues Listify-Konto anlegen diff --git a/listify-client/src/app/auth/verify-email/verify-email.component.html b/listify-client/src/app/auth/verify-email/verify-email.component.html index b189ad2..8b578ae 100644 --- a/listify-client/src/app/auth/verify-email/verify-email.component.html +++ b/listify-client/src/app/auth/verify-email/verify-email.component.html @@ -1,5 +1,8 @@
+ E-Mail-Verifikation {{ email() || 'Listify-Konto bestätigen' }} diff --git a/listify-client/src/app/lists/list-detail/list-detail.component.scss b/listify-client/src/app/lists/list-detail/list-detail.component.scss index 1e602b6..d61eb2f 100644 --- a/listify-client/src/app/lists/list-detail/list-detail.component.scss +++ b/listify-client/src/app/lists/list-detail/list-detail.component.scss @@ -3,6 +3,7 @@ gap: 1rem; width: min(100%, 760px); max-width: 100%; + min-height: calc(100dvh - 56px); margin: 0 auto; padding: 1rem; overflow-x: hidden; @@ -14,13 +15,14 @@ align-items: center; gap: 0.75rem; min-width: 0; + padding: 0.75rem 0 0.25rem; } .detail-header h1 { overflow: hidden; margin: 0; font-size: 1.45rem; - font-weight: 500; + font-weight: 600; text-overflow: ellipsis; white-space: nowrap; } @@ -35,8 +37,11 @@ .items-card { min-width: 0; max-width: 100%; + overflow: hidden; + border: 1px solid color-mix(in srgb, var(--mat-sys-outline-variant) 72%, transparent); border-radius: 8px; - background: color-mix(in srgb, var(--mat-sys-surface-container-low) 94%, white); + background: var(--mat-sys-surface); + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08); } .editor-card mat-card-header { @@ -117,14 +122,15 @@ grid-template-columns: minmax(0, 1fr) auto; gap: 0.35rem 0.5rem; min-width: 0; - padding: 0.5rem; - border: 1px solid var(--mat-sys-outline-variant); + padding: 0.6rem; + border: 1px solid color-mix(in srgb, var(--mat-sys-outline-variant) 72%, transparent); border-radius: 8px; - background: var(--mat-sys-surface); + background: color-mix(in srgb, var(--mat-sys-surface-container-low) 36%, var(--mat-sys-surface)); } .check-items li.checked { - background: color-mix(in srgb, var(--mat-sys-primary) 7%, var(--mat-sys-surface)); + border-color: color-mix(in srgb, var(--mat-sys-primary) 24%, var(--mat-sys-outline-variant)); + background: color-mix(in srgb, var(--mat-sys-primary) 8%, var(--mat-sys-surface)); } .item-title { @@ -162,6 +168,7 @@ @media (min-width: 701px) { .list-detail-page { gap: 1.25rem; + min-height: calc(100dvh - 64px); padding: 2rem; } diff --git a/listify-client/src/app/lists/lists.component.scss b/listify-client/src/app/lists/lists.component.scss index a1eefad..81a9481 100644 --- a/listify-client/src/app/lists/lists.component.scss +++ b/listify-client/src/app/lists/lists.component.scss @@ -3,6 +3,11 @@ gap: 0.75rem; min-width: 0; margin: 0 0 1rem; + padding: 0.75rem; + border: 1px solid color-mix(in srgb, var(--mat-sys-outline-variant) 72%, transparent); + border-radius: 8px; + background: color-mix(in srgb, var(--mat-sys-surface) 88%, transparent); + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.06); } .list-controls mat-form-field { @@ -31,6 +36,8 @@ grid-template-columns: repeat(3, minmax(0, 1fr)); min-width: 0; width: 100%; + overflow: hidden; + border-color: color-mix(in srgb, var(--mat-sys-outline-variant) 72%, transparent); border-radius: 8px; } @@ -48,7 +55,7 @@ } .result-count { - margin: 0 0 0.75rem; + margin: 0 0 0.85rem; color: var(--mat-sys-on-surface-variant); font-size: 0.9rem; } @@ -58,7 +65,8 @@ grid-template-columns: minmax(260px, 1.2fr) minmax(320px, 1fr); align-items: start; gap: 0.85rem 1rem; - margin-bottom: 1.25rem; + margin-bottom: 1.35rem; + padding: 1rem; } .search-field { diff --git a/listify-client/src/app/templates/confirm-delete-dialog/confirm-delete-dialog.component.scss b/listify-client/src/app/templates/confirm-delete-dialog/confirm-delete-dialog.component.scss index 733ae8c..4a1fab3 100644 --- a/listify-client/src/app/templates/confirm-delete-dialog/confirm-delete-dialog.component.scss +++ b/listify-client/src/app/templates/confirm-delete-dialog/confirm-delete-dialog.component.scss @@ -9,7 +9,8 @@ width: 56px; height: 56px; margin: 0 auto 0.25rem; - border-radius: 50%; + border: 1px solid color-mix(in srgb, var(--mat-sys-error) 16%, transparent); + border-radius: 8px; background: color-mix(in srgb, var(--mat-sys-error) 12%, transparent); color: var(--mat-sys-error); } diff --git a/listify-client/src/app/templates/copy-template-dialog/copy-template-dialog.component.scss b/listify-client/src/app/templates/copy-template-dialog/copy-template-dialog.component.scss index 4511ed2..d6080a9 100644 --- a/listify-client/src/app/templates/copy-template-dialog/copy-template-dialog.component.scss +++ b/listify-client/src/app/templates/copy-template-dialog/copy-template-dialog.component.scss @@ -12,3 +12,8 @@ mat-dialog-content { gap: 0.75rem; min-width: min(100vw - 64px, 360px); } + +mat-dialog-actions { + gap: 0.5rem; + padding-bottom: 1rem; +} diff --git a/listify-client/src/app/templates/template-detail/template-detail.component.scss b/listify-client/src/app/templates/template-detail/template-detail.component.scss index bdc438a..812b4ae 100644 --- a/listify-client/src/app/templates/template-detail/template-detail.component.scss +++ b/listify-client/src/app/templates/template-detail/template-detail.component.scss @@ -2,8 +2,11 @@ display: grid; gap: 1rem; width: min(100%, 760px); + max-width: 100%; + min-height: calc(100dvh - 56px); margin: 0 auto; padding: 1rem; + overflow-x: hidden; } .detail-header { @@ -11,13 +14,15 @@ grid-template-columns: auto minmax(0, 1fr); align-items: center; gap: 0.75rem; + min-width: 0; + padding: 0.75rem 0 0.25rem; } .detail-header h1 { overflow: hidden; margin: 0; font-size: 1.45rem; - font-weight: 500; + font-weight: 600; text-overflow: ellipsis; white-space: nowrap; } @@ -41,8 +46,11 @@ .editor-card, .state-card { + overflow: hidden; + border: 1px solid color-mix(in srgb, var(--mat-sys-outline-variant) 72%, transparent); border-radius: 8px; - background: color-mix(in srgb, var(--mat-sys-surface-container-low) 94%, white); + background: var(--mat-sys-surface); + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08); } .template-form, @@ -101,10 +109,10 @@ align-items: center; gap: 0.35rem; min-height: 52px; - padding: 0.35rem; - border: 1px solid var(--mat-sys-outline-variant); + padding: 0.45rem; + border: 1px solid color-mix(in srgb, var(--mat-sys-outline-variant) 72%, transparent); border-radius: 8px; - background: var(--mat-sys-surface); + background: color-mix(in srgb, var(--mat-sys-surface-container-low) 36%, var(--mat-sys-surface)); } .detail-items li.cdk-drag-preview { @@ -158,6 +166,7 @@ @media (min-width: 701px) { .template-detail-page { gap: 1.25rem; + min-height: calc(100dvh - 64px); padding: 2rem; } diff --git a/listify-client/src/app/workspace-page.scss b/listify-client/src/app/workspace-page.scss index f4ab643..a85322f 100644 --- a/listify-client/src/app/workspace-page.scss +++ b/listify-client/src/app/workspace-page.scss @@ -1,6 +1,7 @@ .workspace-page { width: min(100%, 1120px); max-width: 100%; + min-height: calc(100dvh - 56px); margin: 0 auto; padding: 1rem; overflow-x: hidden; @@ -11,15 +12,16 @@ align-items: stretch; flex-direction: column; gap: 1rem; - margin-bottom: 1rem; + margin-bottom: 1.25rem; + padding: 0.75rem 0; min-width: 0; } .page-header h1 { overflow-wrap: anywhere; margin: 0; - font-size: 1.6rem; - font-weight: 500; + font-size: 1.55rem; + font-weight: 600; } .page-header p { @@ -29,8 +31,11 @@ .state-card, .empty-state { + overflow: hidden; + border: 1px solid color-mix(in srgb, var(--mat-sys-outline-variant) 72%, transparent); border-radius: 8px; - background: color-mix(in srgb, var(--mat-sys-surface-container-low) 94%, white); + background: var(--mat-sys-surface); + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08); } .state-card mat-card-content, @@ -77,13 +82,24 @@ .template-card { min-width: 0; max-width: 100%; + overflow: hidden; + border: 1px solid color-mix(in srgb, var(--mat-sys-outline-variant) 72%, transparent); border-radius: 8px; - background: color-mix(in srgb, var(--mat-sys-surface-container-low) 94%, white); + background: var(--mat-sys-surface); + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08); + transition: + box-shadow 0.2s ease, + transform 0.2s ease; +} + +.template-card:hover { + box-shadow: 0 8px 25px rgba(0, 0, 0, 0.12); + transform: translateY(-1px); } .template-card mat-card-header { min-width: 0; - padding-bottom: 0.35rem; + padding-bottom: 0.25rem; } .template-card mat-card-title, @@ -95,6 +111,7 @@ margin: 0.5rem 0 0; color: var(--mat-sys-on-surface-variant); overflow-wrap: anywhere; + line-height: 1.45; } .template-meta { @@ -151,6 +168,7 @@ @media (min-width: 701px) { .workspace-page { + min-height: calc(100dvh - 64px); padding: 2rem; } @@ -158,7 +176,7 @@ align-items: center; flex-direction: row; justify-content: space-between; - margin-bottom: 1.5rem; + margin-bottom: 1.75rem; } .page-header h1 {