Files
hotline-planner/dev/backend/HotlinePlanner profile pic and bg worker/Views/Home/Index.cshtml
2026-02-24 13:32:01 +01:00

183 lines
9.0 KiB
Plaintext

@{
ViewData["Title"] = "Dashboard";
var portraitUrl = User.FindFirst("app.picture")?.Value
?? User.FindFirst(System.Security.Claims.ClaimTypes.Uri)?.Value;
var isSubscribed = ViewData["IsSubscribed"] as bool? ?? false;
var lastRun = ViewData["WorkerLastRun"] as string;
var nextRun = ViewData["WorkerNextRun"] as string;
var lastResult = ViewData["WorkerLastResult"] as string;
}
<div class="row q-col-gutter-xl justify-center items-center" style="min-height: 70vh">
<div class="col-12 col-md-8 col-lg-6">
<!-- HEADER -->
<div class="text-center q-mb-xl">
<h1 class="text-h3 text-weight-bold text-primary q-mb-sm">NextGen Dashboard</h1>
<p class="text-subtitle1 text-grey-7">Real-time enterprise calendar synchronization</p>
</div>
<!-- AUTHENTICATED VIEW -->
<div v-if="isAuthenticated" class="animate__animated animate__fadeIn">
<q-card flat class="clean-card q-mb-lg overflow-hidden">
<q-card-section class="q-pa-xl bg-grey-1 text-center border-bottom">
@if (!string.IsNullOrEmpty(portraitUrl))
{
<q-avatar size="100px" class="q-mb-md shadow-1 border-white">
<img src="@portraitUrl">
</q-avatar>
}
else
{
<q-avatar size="100px" font-size="48px" color="primary" text-color="white" icon="person" class="q-mb-md shadow-1"></q-avatar>
}
<div class="text-h5 text-weight-bold text-grey-9">{{userName}}</div>
<div class="text-caption text-grey-6 text-uppercase q-mt-xs" style="letter-spacing: 1px">Active Session</div>
<q-btn flat dense rounded color="grey-6" icon="refresh" label="Update Photo" size="sm" class="q-mt-md" v-on:click="refreshPhoto"></q-btn>
</q-card-section>
<q-card-section class="q-pa-lg">
<div class="row items-center justify-between q-mb-lg">
<div class="text-subtitle1 text-weight-bold">Engine Status</div>
<q-chip :color="isSubscribed ? 'positive' : 'grey-4'"
:text-color="isSubscribed ? 'white' : 'grey-9'"
:icon="isSubscribed ? 'check_circle' : 'pause_circle'"
class="q-px-md">
{{ isSubscribed ? 'Running' : 'Paused' }}
</q-chip>
</div>
<div class="bg-grey-1 q-pa-md rounded-borders border">
<div class="row q-col-gutter-md">
<div class="col-4 text-center border-right">
<div class="text-caption text-grey-6 uppercase">Last Sync</div>
<div class="text-weight-bold text-grey-9">{{ lastRun || 'Waiting...' }}</div>
</div>
<div class="col-4 text-center border-right">
<div class="text-caption text-grey-6 uppercase">Next Run</div>
<div class="text-weight-bold text-grey-9 text-primary">{{ nextRun || 'Scheduled' }}</div>
</div>
<div class="col-4 text-center">
<div class="text-caption text-grey-6 uppercase">Health</div>
<div>
<q-badge :color="lastResult === 'Success' ? 'positive' : 'negative'" v-if="lastResult" rounded>
{{ lastResult }}
</q-badge>
<span v-else class="text-grey-4">—</span>
</div>
</div>
</div>
</div>
</q-card-section>
<q-card-actions align="center" class="q-pb-xl q-px-xl">
<q-btn :label="isSubscribed ? 'Stop Sync Engine' : 'Start Sync Engine'"
:color="isSubscribed ? 'negative' : 'primary'"
:icon="isSubscribed ? 'stop' : 'play_arrow'"
unelevated
rounded
size="lg"
class="full-width q-py-md text-weight-bold"
v-on:click="toggleSubscription"></q-btn>
</q-card-actions>
</q-card>
<div class="row q-col-gutter-md">
<div class="col-12 col-sm-4">
<q-btn outline color="secondary" icon="update" label="Manual Sync" class="full-width q-py-md rounded-borders" v-on:click="createEvent"></q-btn>
</div>
<div class="col-12 col-sm-4">
<q-btn outline color="grey-7" icon="event" label="View Logs" class="full-width q-py-md rounded-borders" href="/Account/EventLogs"></q-btn>
</div>
<div class="col-12 col-sm-4">
<q-btn flat color="grey-6" icon="logout" label="End Session" class="full-width q-py-md rounded-borders" v-on:click="logout"></q-btn>
</div>
</div>
</div>
<!-- LOGIN VIEW -->
<div v-else class="animate__animated animate__fadeIn">
<q-card flat class="clean-card q-pa-xl text-center">
<q-avatar size="80px" color="blue-1" text-color="primary" icon="security" class="q-mb-lg"></q-avatar>
<div class="text-h5 text-weight-bold q-mb-sm text-grey-9">Identity Portal</div>
<p class="text-body1 text-grey-7 q-mb-xl">Please sign in with your enterprise credentials to access the NextGen synchronization platform.</p>
<div class="column q-gutter-md">
<q-btn unelevated color="primary" size="lg" rounded v-on:click="login('Microsoft')" class="full-width q-py-md shadow-1">
<q-icon name="widgets" class="q-mr-sm"></q-icon>
Connect via Microsoft 365
</q-btn>
<q-btn outline color="grey-7" size="lg" rounded v-on:click="login('Google')" class="full-width q-py-md">
<q-icon name="login" class="q-mr-sm" color="negative"></q-icon>
Connect via Google Workspace
</q-btn>
</div>
<div class="q-mt-xl text-caption text-grey-5 flex flex-center items-center">
<q-icon name="lock" class="q-mr-xs"></q-icon> Protected by secure RSA 4096-bit encryption
</div>
</q-card>
</div>
</div>
</div>
<style>
.border-bottom { border-bottom: 1px solid rgba(0,0,0,0.05); }
.border-right { border-right: 1px solid rgba(0,0,0,0.05); }
.border { border: 1px solid rgba(0,0,0,0.05); }
.uppercase { text-transform: uppercase; font-size: 0.65rem; letter-spacing: 1px; margin-bottom: 4px; }
</style>
@section Scripts {
<script>
app.mixin({
data() {
return {
isSubscribed: @(isSubscribed.ToString().ToLower()),
lastRun: '@lastRun',
nextRun: '@nextRun',
lastResult: '@lastResult'
}
},
methods: {
login(provider) {
const form = document.createElement('form');
form.method = 'POST';
form.action = '/Account/ExternalLogin';
const providerInput = document.createElement('input');
providerInput.type = 'hidden';
providerInput.name = 'provider';
providerInput.value = provider;
form.appendChild(providerInput);
document.body.appendChild(form);
form.submit();
},
toggleSubscription() {
const form = document.createElement('form');
form.method = 'POST';
form.action = '/Account/ToggleSubscription';
document.body.appendChild(form);
form.submit();
},
createEvent() {
const form = document.createElement('form');
form.method = 'POST';
form.action = '/Account/CreateCalendarEvent';
document.body.appendChild(form);
form.submit();
},
refreshPhoto() {
const form = document.createElement('form');
form.method = 'POST';
form.action = '/Account/SyncProfilePicture';
document.body.appendChild(form);
form.submit();
}
}
});
</script>
}