183 lines
9.0 KiB
Plaintext
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>
|
|
}
|