Refactors admin page design to move each 'facet' of admin into its own component.
This commit is contained in:
1 parent
a4093e86b1
commit
c02c0cdde9
30 files changed
+1970
-1757
No files matched your search
@@ -0,0 +1,218 @@
|
||||
<div class="tab-content mobile-builds-panel">
|
||||
<div class="tab-toolbar">
|
||||
<h2>Mobile Build Orchestration</h2>
|
||||
<button class="btn btn-add" (click)="reloadMobileBuilds()">Refresh Requests</button>
|
||||
</div>
|
||||
|
||||
<div class="mobile-build-grid">
|
||||
<section class="build-card">
|
||||
<h3>Derived Defaults</h3>
|
||||
@if (mobileBuildDefaults; as defaults) {
|
||||
<div class="derived-list">
|
||||
<div><strong>Tenant:</strong> {{ defaults.tenant_key }}</div>
|
||||
<div><strong>App Name:</strong> {{ defaults.app_name }}</div>
|
||||
<div><strong>Support Email:</strong> {{ defaults.support_email }}</div>
|
||||
<div><strong>Website:</strong> {{ defaults.website }}</div>
|
||||
<div><strong>Stream URL:</strong> {{ defaults.stream_url || '(not set)' }}</div>
|
||||
</div>
|
||||
} @else {
|
||||
<p class="empty-row">Defaults unavailable. Check station config.</p>
|
||||
}
|
||||
</section>
|
||||
|
||||
<section class="build-card">
|
||||
<h3>Theme Export</h3>
|
||||
@if (themeJsonStatus$ | async; as status) {
|
||||
@if (status.exists) {
|
||||
<div class="theme-status-ok">
|
||||
<div><strong>theme.json</strong> — ready</div>
|
||||
<div>Path: {{ status.path }}</div>
|
||||
<div>Generated: {{ status.generated_at }}</div>
|
||||
<div>Size: {{ status.size }} bytes</div>
|
||||
</div>
|
||||
} @else {
|
||||
<div class="theme-status-missing">
|
||||
<p><strong>theme.json</strong> not found at {{ status.path }}</p>
|
||||
<p>Regenerate to create it from the current station config.</p>
|
||||
</div>
|
||||
}
|
||||
<button class="btn btn-outline" (click)="regenerateThemeJson()"
|
||||
[disabled]="themeJsonLoading$ | async">
|
||||
{{ (themeJsonLoading$ | async) ? 'Regenerating...' : 'Regenerate' }}
|
||||
</button>
|
||||
} @else {
|
||||
@if (themeJsonLoading$ | async) {
|
||||
<p class="empty-row">Loading...</p>
|
||||
}
|
||||
}
|
||||
</section>
|
||||
|
||||
<section class="build-card">
|
||||
<h3>Create Build Request</h3>
|
||||
|
||||
<div class="form-row platform-row">
|
||||
<label>
|
||||
<input type="checkbox" [(ngModel)]="buildAndroid">
|
||||
Android
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" [(ngModel)]="buildIos">
|
||||
iOS
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label>
|
||||
Android Application ID
|
||||
<input type="text" [(ngModel)]="androidApplicationId" placeholder="com.example.radio">
|
||||
</label>
|
||||
<label>
|
||||
iOS Bundle ID
|
||||
<input type="text" [(ngModel)]="iosBundleId" placeholder="com.example.radio">
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label>
|
||||
Version Name
|
||||
<input type="text" [(ngModel)]="buildVersionName" placeholder="1.0.0">
|
||||
</label>
|
||||
<label>
|
||||
Build Number
|
||||
<input type="text" [(ngModel)]="buildNumber" placeholder="1">
|
||||
</label>
|
||||
<label>
|
||||
Release Profile
|
||||
<input type="text" [(ngModel)]="buildReleaseProfile" placeholder="release">
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<h4>Required Files</h4>
|
||||
<div class="form-row">
|
||||
<label>
|
||||
Android Keystore (.jks/.keystore)
|
||||
<input type="file" (change)="onFileChosen($event, 'android-keystore')">
|
||||
</label>
|
||||
<label>
|
||||
Android Descriptor (JSON)
|
||||
<input type="file" accept=".json,application/json" (change)="onFileChosen($event, 'android-descriptor')">
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label>
|
||||
iOS Certificate (.p12)
|
||||
<input type="file" accept=".p12" (change)="onFileChosen($event, 'ios-certificate')">
|
||||
</label>
|
||||
<label>
|
||||
iOS Provisioning Profile (.mobileprovision)
|
||||
<input type="file" accept=".mobileprovision" (change)="onFileChosen($event, 'ios-profile')">
|
||||
</label>
|
||||
</div>
|
||||
|
||||
@if (mobileBuildStatusMessage) {
|
||||
<p class="status success">{{ mobileBuildStatusMessage }}</p>
|
||||
}
|
||||
@if (mobileBuildErrorMessage) {
|
||||
<p class="status error">{{ mobileBuildErrorMessage }}</p>
|
||||
}
|
||||
|
||||
<button class="btn btn-add" [disabled]="mobileBuildBusy" (click)="createAndUploadMobileBuildRequest()">
|
||||
{{ mobileBuildBusy ? 'Working…' : 'Create Draft + Upload Files' }}
|
||||
</button>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<section class="build-card">
|
||||
<h3>Build Requests</h3>
|
||||
@if (mobileBuilds$ | async; as builds) {
|
||||
<table class="admin-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Platforms</th>
|
||||
<th>Version</th>
|
||||
<th>Status</th>
|
||||
<th>Preflight</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@for (build of builds; track build.id) {
|
||||
<tr [class.selected-build]="selectedMobileBuildId === build.id">
|
||||
<td>#{{ build.id }}</td>
|
||||
<td>
|
||||
{{ build.platform_android ? 'Android' : '' }}
|
||||
{{ build.platform_android && build.platform_ios ? ' + ' : '' }}
|
||||
{{ build.platform_ios ? 'iOS' : '' }}
|
||||
</td>
|
||||
<td>{{ build.version_name }} ({{ build.build_number }})</td>
|
||||
<td>{{ build.status }}</td>
|
||||
<td>{{ build.preflight_passed ? 'Passed' : 'Pending/Failed' }}</td>
|
||||
<td class="actions">
|
||||
<button class="btn-icon btn-edit" (click)="onMobileBuildSelected(build.id)">Select</button>
|
||||
</td>
|
||||
</tr>
|
||||
} @empty {
|
||||
<tr>
|
||||
<td colspan="6" class="empty-row">No build requests yet.</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
</section>
|
||||
|
||||
@if (selectedMobileBuild; as build) {
|
||||
<section class="build-card">
|
||||
<div class="tab-toolbar compact">
|
||||
<h3>Selected Request #{{ build.id }}</h3>
|
||||
<div class="action-group">
|
||||
<button class="btn-icon btn-edit" [disabled]="mobileBuildBusy" (click)="runPreflightForSelectedBuild()">Run
|
||||
Preflight</button>
|
||||
<button class="btn-icon btn-edit" [disabled]="mobileBuildBusy || !build.preflight_passed"
|
||||
(click)="triggerSelectedBuild()">Trigger Build</button>
|
||||
<button class="btn-icon btn-edit" [disabled]="mobileBuildBusy"
|
||||
(click)="refreshSelectedBuild()">Refresh</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (mobileBuildPreflightIssues.length > 0) {
|
||||
<div class="status error">
|
||||
<strong>Preflight issues:</strong>
|
||||
<ul>
|
||||
@for (issue of mobileBuildPreflightIssues; track issue) {
|
||||
<li>{{ issue }}</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (build.error_message) {
|
||||
<p class="status error"><strong>Error:</strong> {{ build.error_message }}</p>
|
||||
}
|
||||
|
||||
<h4>Artifacts</h4>
|
||||
<div class="artifact-list">
|
||||
@for (artifact of build.artifacts; track artifact.id) {
|
||||
<div class="artifact-row">
|
||||
<div>
|
||||
<strong>{{ artifact.file_name }}</strong>
|
||||
<div class="artifact-meta">{{ artifact.platform }} • {{ artifact.artifact_type }} • {{
|
||||
artifact.sha256.slice(0, 12) }}… • {{ formatFileSize(artifact.size) }}</div>
|
||||
</div>
|
||||
<button class="btn-icon btn-edit"
|
||||
(click)="downloadArtifact(artifact)">
|
||||
Download
|
||||
</button>
|
||||
</div>
|
||||
} @empty {
|
||||
<p class="empty-row">No artifacts available yet.</p>
|
||||
}
|
||||
</div>
|
||||
|
||||
<h4>Build Log</h4>
|
||||
<div class="build-log"><pre>{{ build.build_log || 'No logs yet.' }}</pre></div>
|
||||
</section>
|
||||
}
|
||||
</div>
|
||||
Reference in new issue
Block a user