feat(phase2): localization EN/RU + additional widget types
- Add svelte-i18n with 224 translation keys (English + Russian) - Language switcher in header (EN/RU toggle, persists to localStorage) - Extract all hardcoded strings from 37 component/page files - Add 4 new widget types: Bookmark, Note (markdown), Embed (iframe), Status - WidgetRenderer dispatches by type, WidgetGrid supports full-width widgets - Type-specific config forms in board editor - Install marked for markdown rendering
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { t } from 'svelte-i18n';
|
||||
import { EntityType, TargetType, PermissionLevel } from '$lib/utils/constants.js';
|
||||
|
||||
interface PermissionRecord {
|
||||
@@ -95,69 +96,69 @@
|
||||
<div class="space-y-4">
|
||||
<!-- Grant form -->
|
||||
<div class="rounded-lg border border-border bg-card p-4">
|
||||
<h3 class="mb-3 text-sm font-semibold text-card-foreground">Grant Permission</h3>
|
||||
<h3 class="mb-3 text-sm font-semibold text-card-foreground">{$t('admin.perm_title')}</h3>
|
||||
<div class="grid grid-cols-1 gap-3 sm:grid-cols-5">
|
||||
<div>
|
||||
<label for="perm-entity-type" class="mb-1 block text-xs text-muted-foreground">Entity Type</label>
|
||||
<label for="perm-entity-type" class="mb-1 block text-xs text-muted-foreground">{$t('admin.perm_entity_type')}</label>
|
||||
<select
|
||||
id="perm-entity-type"
|
||||
bind:value={selectedEntityType}
|
||||
onchange={() => (selectedEntityId = '')}
|
||||
class="w-full rounded border border-input bg-background px-2 py-1.5 text-sm text-foreground"
|
||||
>
|
||||
<option value={EntityType.BOARD}>Board</option>
|
||||
<option value={EntityType.APP}>App</option>
|
||||
<option value={EntityType.BOARD}>{$t('admin.perm_board')}</option>
|
||||
<option value={EntityType.APP}>{$t('admin.perm_app')}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label for="perm-entity" class="mb-1 block text-xs text-muted-foreground">Entity</label>
|
||||
<label for="perm-entity" class="mb-1 block text-xs text-muted-foreground">{$t('admin.perm_entity')}</label>
|
||||
<select
|
||||
id="perm-entity"
|
||||
bind:value={selectedEntityId}
|
||||
class="w-full rounded border border-input bg-background px-2 py-1.5 text-sm text-foreground"
|
||||
>
|
||||
<option value="" disabled>Select...</option>
|
||||
<option value="" disabled>{$t('admin.perm_select')}</option>
|
||||
{#each entityOptions as option (option.id)}
|
||||
<option value={option.id}>{option.name}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label for="perm-target-type" class="mb-1 block text-xs text-muted-foreground">Target Type</label>
|
||||
<label for="perm-target-type" class="mb-1 block text-xs text-muted-foreground">{$t('admin.perm_target_type')}</label>
|
||||
<select
|
||||
id="perm-target-type"
|
||||
bind:value={selectedTargetType}
|
||||
onchange={() => (selectedTargetId = '')}
|
||||
class="w-full rounded border border-input bg-background px-2 py-1.5 text-sm text-foreground"
|
||||
>
|
||||
<option value={TargetType.USER}>User</option>
|
||||
<option value={TargetType.GROUP}>Group</option>
|
||||
<option value={TargetType.USER}>{$t('admin.perm_user')}</option>
|
||||
<option value={TargetType.GROUP}>{$t('admin.perm_group')}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label for="perm-target" class="mb-1 block text-xs text-muted-foreground">Target</label>
|
||||
<label for="perm-target" class="mb-1 block text-xs text-muted-foreground">{$t('admin.perm_target')}</label>
|
||||
<select
|
||||
id="perm-target"
|
||||
bind:value={selectedTargetId}
|
||||
class="w-full rounded border border-input bg-background px-2 py-1.5 text-sm text-foreground"
|
||||
>
|
||||
<option value="" disabled>Select...</option>
|
||||
<option value="" disabled>{$t('admin.perm_select')}</option>
|
||||
{#each targetOptions as option (option.id)}
|
||||
<option value={option.id}>{option.name}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label for="perm-level" class="mb-1 block text-xs text-muted-foreground">Level</label>
|
||||
<label for="perm-level" class="mb-1 block text-xs text-muted-foreground">{$t('admin.perm_level')}</label>
|
||||
<div class="flex gap-1">
|
||||
<select
|
||||
id="perm-level"
|
||||
bind:value={selectedLevel}
|
||||
class="w-full rounded border border-input bg-background px-2 py-1.5 text-sm text-foreground"
|
||||
>
|
||||
<option value={PermissionLevel.VIEW}>View</option>
|
||||
<option value={PermissionLevel.EDIT}>Edit</option>
|
||||
<option value={PermissionLevel.ADMIN}>Admin</option>
|
||||
<option value={PermissionLevel.VIEW}>{$t('admin.perm_view')}</option>
|
||||
<option value={PermissionLevel.EDIT}>{$t('admin.perm_edit')}</option>
|
||||
<option value={PermissionLevel.ADMIN}>{$t('admin.perm_admin')}</option>
|
||||
</select>
|
||||
<button
|
||||
type="button"
|
||||
@@ -165,7 +166,7 @@
|
||||
disabled={!selectedEntityId || !selectedTargetId}
|
||||
class="shrink-0 rounded bg-primary px-3 py-1.5 text-xs font-medium text-primary-foreground hover:bg-primary/90 disabled:opacity-50"
|
||||
>
|
||||
Grant
|
||||
{$t('admin.perm_grant')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -178,10 +179,10 @@
|
||||
<table class="w-full text-left text-sm">
|
||||
<thead class="border-b border-border bg-muted/50">
|
||||
<tr>
|
||||
<th class="px-4 py-2 text-xs font-medium text-muted-foreground">Entity</th>
|
||||
<th class="px-4 py-2 text-xs font-medium text-muted-foreground">Target</th>
|
||||
<th class="px-4 py-2 text-xs font-medium text-muted-foreground">Level</th>
|
||||
<th class="px-4 py-2 text-xs font-medium text-muted-foreground">Action</th>
|
||||
<th class="px-4 py-2 text-xs font-medium text-muted-foreground">{$t('admin.perm_entity_column')}</th>
|
||||
<th class="px-4 py-2 text-xs font-medium text-muted-foreground">{$t('admin.perm_target_column')}</th>
|
||||
<th class="px-4 py-2 text-xs font-medium text-muted-foreground">{$t('admin.perm_level_column')}</th>
|
||||
<th class="px-4 py-2 text-xs font-medium text-muted-foreground">{$t('admin.perm_action_column')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -206,7 +207,7 @@
|
||||
onclick={() => handleRevoke(perm)}
|
||||
class="text-xs text-destructive hover:underline"
|
||||
>
|
||||
Revoke
|
||||
{$t('admin.perm_revoke')}
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -215,6 +216,6 @@
|
||||
</table>
|
||||
</div>
|
||||
{:else}
|
||||
<p class="text-sm text-muted-foreground">No permissions configured.</p>
|
||||
<p class="text-sm text-muted-foreground">{$t('admin.perm_none')}</p>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user