|
| 1 | +@if (provider !== 'platform') { |
| 2 | + <div class="text-sm text-gray-500"> |
| 3 | + Table management is only available for Platform databases. |
| 4 | + </div> |
| 5 | +} @else { |
| 6 | + <div class="flex justify-between items-center mb-4"> |
| 7 | + <h5 class="text-sm font-medium text-gray-700 dark:text-gray-300">Tables</h5> |
| 8 | + <button class="btn-outline text-sm" (click)="openCreateModal()"> |
| 9 | + + New Table |
| 10 | + </button> |
| 11 | + </div> |
| 12 | + |
| 13 | + @if (isLoading()) { |
| 14 | + <div class="text-sm text-gray-500">Loading...</div> |
| 15 | + } @else if (error()) { |
| 16 | + <div class="text-sm text-red-500">{{ error() }}</div> |
| 17 | + } @else if (tables().length === 0) { |
| 18 | + <div class="text-sm text-gray-500">No tables yet. Create your first table to get started.</div> |
| 19 | + } @else { |
| 20 | + <div class="space-y-2"> |
| 21 | + @for (table of tables(); track table.name) { |
| 22 | + <div |
| 23 | + class="border rounded-lg overflow-hidden dark:border-gray-700" |
| 24 | + [class.border-blue-500]="selectedTable() === table.name" |
| 25 | + > |
| 26 | + <div |
| 27 | + class="flex items-center justify-between px-3 py-2 cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800" |
| 28 | + (click)="selectTable(table.name)" |
| 29 | + > |
| 30 | + <div class="flex items-center gap-2"> |
| 31 | + <span class="font-mono text-sm">{{ table.name }}</span> |
| 32 | + <span class="text-xs text-gray-500">({{ table.rowCount }} rows)</span> |
| 33 | + </div> |
| 34 | + <button |
| 35 | + class="text-red-500 hover:text-red-700 text-sm px-2" |
| 36 | + (click)="confirmDelete(table.name, $event)" |
| 37 | + title="Delete table" |
| 38 | + > |
| 39 | + × |
| 40 | + </button> |
| 41 | + </div> |
| 42 | + |
| 43 | + @if (selectedTable() === table.name && selectedColumns().length > 0) { |
| 44 | + <div class="border-t dark:border-gray-700 bg-gray-50 dark:bg-gray-800 px-3 py-2"> |
| 45 | + <table class="w-full text-xs"> |
| 46 | + <thead> |
| 47 | + <tr class="text-left text-gray-500"> |
| 48 | + <th class="pb-1">Column</th> |
| 49 | + <th class="pb-1">Type</th> |
| 50 | + <th class="pb-1">Nullable</th> |
| 51 | + <th class="pb-1">Default</th> |
| 52 | + <th class="pb-1 w-8"></th> |
| 53 | + </tr> |
| 54 | + </thead> |
| 55 | + <tbody> |
| 56 | + @for (col of selectedColumns(); track col.name) { |
| 57 | + <tr class="group"> |
| 58 | + <td class="py-0.5 font-mono"> |
| 59 | + {{ col.name }} |
| 60 | + @if (col.primaryKey) { |
| 61 | + <span class="text-yellow-600 ml-1" title="Primary Key">🔑</span> |
| 62 | + } |
| 63 | + </td> |
| 64 | + <td class="py-0.5 text-gray-600 dark:text-gray-400">{{ col.type }}</td> |
| 65 | + <td class="py-0.5">{{ col.nullable ? 'Yes' : 'No' }}</td> |
| 66 | + <td class="py-0.5 text-gray-500 font-mono max-w-[120px] truncate" [title]="col.defaultValue || ''">{{ col.defaultValue || '-' }}</td> |
| 67 | + <td class="py-0.5 text-right"> |
| 68 | + @if (!col.primaryKey) { |
| 69 | + <button |
| 70 | + class="text-red-500 hover:text-red-700 opacity-0 group-hover:opacity-100 transition-opacity" |
| 71 | + (click)="confirmDeleteColumn(col.name, $event)" |
| 72 | + title="Drop column" |
| 73 | + > |
| 74 | + × |
| 75 | + </button> |
| 76 | + } |
| 77 | + </td> |
| 78 | + </tr> |
| 79 | + } |
| 80 | + </tbody> |
| 81 | + </table> |
| 82 | + <button class="text-xs text-blue-600 hover:text-blue-800 mt-2" (click)="openAddColumnModal()"> |
| 83 | + + Add Column |
| 84 | + </button> |
| 85 | + </div> |
| 86 | + } |
| 87 | + </div> |
| 88 | + } |
| 89 | + </div> |
| 90 | + } |
| 91 | +} |
| 92 | + |
| 93 | +<!-- Create Table Modal --> |
| 94 | +<app-create-table-modal |
| 95 | + [open]="showCreateModal()" |
| 96 | + [databaseId]="databaseId" |
| 97 | + (openChange)="showCreateModal.set($event)" |
| 98 | + (created)="onTableCreated()" |
| 99 | +/> |
| 100 | + |
| 101 | +<!-- Delete Table Confirmation Modal --> |
| 102 | +<app-modal [open]="showDeleteConfirm()" (openChange)="showDeleteConfirm.set($event)" variant="small"> |
| 103 | + <div role="header"> |
| 104 | + <h4>Delete Table</h4> |
| 105 | + </div> |
| 106 | + <div role="body"> |
| 107 | + <p>Are you sure you want to delete table <strong class="font-mono">{{ tableToDelete() }}</strong>?</p> |
| 108 | + <p class="text-sm text-red-600 mt-2">This action cannot be undone. All data will be permanently deleted.</p> |
| 109 | + </div> |
| 110 | + <div role="footer" class="flex gap-2 justify-end"> |
| 111 | + <button class="btn-outline" (click)="showDeleteConfirm.set(false)">Cancel</button> |
| 112 | + <button class="btn-primary bg-red-600 hover:bg-red-700" (click)="deleteTable()">Delete</button> |
| 113 | + </div> |
| 114 | +</app-modal> |
| 115 | + |
| 116 | +<!-- Add Column Modal --> |
| 117 | +<app-modal [open]="showAddColumnModal()" (openChange)="showAddColumnModal.set($event)" variant="small"> |
| 118 | + <div role="header"> |
| 119 | + <h4>Add Column to {{ selectedTable() }}</h4> |
| 120 | + </div> |
| 121 | + <div role="body" class="space-y-3"> |
| 122 | + <div> |
| 123 | + <label class="block text-sm font-medium mb-1">Column Name</label> |
| 124 | + <input |
| 125 | + type="text" |
| 126 | + class="input-outline w-full font-mono" |
| 127 | + [(ngModel)]="newColumn.name" |
| 128 | + placeholder="column_name" |
| 129 | + pattern="^[a-zA-Z_][a-zA-Z0-9_]*$" |
| 130 | + /> |
| 131 | + </div> |
| 132 | + <div> |
| 133 | + <label class="block text-sm font-medium mb-1">Type</label> |
| 134 | + <select class="input-outline w-full" [(ngModel)]="newColumn.type"> |
| 135 | + @for (type of commonTypes; track type) { |
| 136 | + <option [value]="type">{{ type }}</option> |
| 137 | + } |
| 138 | + </select> |
| 139 | + </div> |
| 140 | + <div class="flex gap-4 text-sm"> |
| 141 | + <label class="flex items-center gap-1 cursor-pointer"> |
| 142 | + <input type="checkbox" [(ngModel)]="newColumn.notNull" /> |
| 143 | + NOT NULL |
| 144 | + </label> |
| 145 | + <label class="flex items-center gap-1 cursor-pointer"> |
| 146 | + <input type="checkbox" [(ngModel)]="newColumn.unique" /> |
| 147 | + UNIQUE |
| 148 | + </label> |
| 149 | + </div> |
| 150 | + </div> |
| 151 | + <div role="footer" class="flex gap-2 justify-end"> |
| 152 | + <button class="btn-outline" (click)="showAddColumnModal.set(false)">Cancel</button> |
| 153 | + <button class="btn-primary" [disabled]="!newColumn.name.trim()" (click)="addColumn()">Add Column</button> |
| 154 | + </div> |
| 155 | +</app-modal> |
| 156 | + |
| 157 | +<!-- Delete Column Confirmation Modal --> |
| 158 | +<app-modal [open]="showDeleteColumnConfirm()" (openChange)="showDeleteColumnConfirm.set($event)" variant="small"> |
| 159 | + <div role="header"> |
| 160 | + <h4>Drop Column</h4> |
| 161 | + </div> |
| 162 | + <div role="body"> |
| 163 | + <p>Are you sure you want to drop column <strong class="font-mono">{{ columnToDelete() }}</strong>?</p> |
| 164 | + <p class="text-sm text-red-600 mt-2">This action cannot be undone. All data in this column will be permanently deleted.</p> |
| 165 | + </div> |
| 166 | + <div role="footer" class="flex gap-2 justify-end"> |
| 167 | + <button class="btn-outline" (click)="showDeleteColumnConfirm.set(false)">Cancel</button> |
| 168 | + <button class="btn-primary bg-red-600 hover:bg-red-700" (click)="deleteColumn()">Drop Column</button> |
| 169 | + </div> |
| 170 | +</app-modal> |
0 commit comments