223 lines
8.9 KiB
Plaintext
223 lines
8.9 KiB
Plaintext
@model ManageAccountViewModel
|
|
@{
|
|
ViewData["Title"] = "Manage Account";
|
|
var subscription = Model.Subscription;
|
|
var trial = Model.Trial;
|
|
}
|
|
|
|
<div class="page-heading compact">
|
|
<div>
|
|
<p class="eyebrow">Account</p>
|
|
<h1>Manage Account</h1>
|
|
<p class="lead-text">@Model.User.Email</p>
|
|
</div>
|
|
</div>
|
|
|
|
@if (TempData["AuthMessage"] is string authMessage)
|
|
{
|
|
<div class="alert alert-info">@authMessage</div>
|
|
}
|
|
@if (TempData["AuthError"] is string authError)
|
|
{
|
|
<div class="alert alert-warning">@authError</div>
|
|
}
|
|
|
|
<section class="edit-panel">
|
|
<h2>Account security</h2>
|
|
<div class="row g-3 mb-3">
|
|
<div class="col-md-4">
|
|
<p class="eyebrow">Email address</p>
|
|
<p>@Model.User.Email</p>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<p class="eyebrow">Email verified</p>
|
|
<p>@(Model.User.EmailConfirmed ? "Yes" : "No")</p>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<p class="eyebrow">Two-factor authentication</p>
|
|
<p>@(Model.User.TwoFactorEnabled ? "Enabled" : "Not enabled")</p>
|
|
</div>
|
|
</div>
|
|
<div class="button-row mb-4">
|
|
<a class="btn btn-outline-primary" asp-action="ChangePassword">Change Password</a>
|
|
</div>
|
|
|
|
<h2>Two-Factor Authentication</h2>
|
|
<p class="muted">Use an authenticator app to add an extra sign-in step for your account.</p>
|
|
<div class="button-row">
|
|
@if (Model.User.TwoFactorEnabled)
|
|
{
|
|
<a class="btn btn-outline-primary" asp-action="RecoveryCodes">Recovery Codes</a>
|
|
<a class="btn btn-outline-danger" asp-action="DisableTwoFactor">Disable Two Factor</a>
|
|
}
|
|
else
|
|
{
|
|
<a class="btn btn-primary" asp-action="EnableTwoFactor">Enable Two Factor</a>
|
|
}
|
|
</div>
|
|
</section>
|
|
|
|
<section class="edit-panel">
|
|
<h2>Subscription</h2>
|
|
@if (subscription is null)
|
|
{
|
|
<p class="muted">No active subscription is assigned.</p>
|
|
}
|
|
else
|
|
{
|
|
@if (trial is not null)
|
|
{
|
|
<div class="trial-status-panel @trial.WarningCssClass">
|
|
<div class="trial-status-panel__heading">
|
|
<div>
|
|
<p class="eyebrow">Free trial status</p>
|
|
<h3>@trial.StatusLabel</h3>
|
|
</div>
|
|
<a class="btn btn-outline-primary btn-sm" asp-action="Subscription">Upgrade</a>
|
|
</div>
|
|
<p>Your 30-day @trial.PlanName trial expires on @FormatDate(trial.ExpiryDateUtc).</p>
|
|
<div class="trial-progress" aria-label="Trial progress">
|
|
<div class="trial-progress__bar" style="width: @trial.ProgressPercentage%"></div>
|
|
</div>
|
|
</div>
|
|
}
|
|
<div class="row g-3">
|
|
<div class="col-md-4">
|
|
<p class="eyebrow">Current plan</p>
|
|
<p>@subscription.DisplayName</p>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<p class="eyebrow">Trial start</p>
|
|
<p>@(trial is null ? "Not on trial" : FormatDate(trial.StartDateUtc))</p>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<p class="eyebrow">Trial expiry</p>
|
|
<p>@(trial is null ? "Not on trial" : FormatDate(trial.ExpiryDateUtc))</p>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<p class="eyebrow">Days remaining</p>
|
|
<p>@(trial is null ? "Not on trial" : trial.StatusLabel)</p>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<p class="eyebrow">Books</p>
|
|
<p>@FormatLimit(subscription.MaxBooks)</p>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<p class="eyebrow">Storage</p>
|
|
<p>@FormatStorage(subscription.MaxStorageMB)</p>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<p class="eyebrow">Chapters per book</p>
|
|
<p>@FormatLimit(subscription.MaxChaptersPerBook)</p>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<p class="eyebrow">Scenes per book</p>
|
|
<p>@FormatLimit(subscription.MaxScenesPerBook)</p>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<p class="eyebrow">Characters per project</p>
|
|
<p>@FormatLimit(subscription.MaxCharactersPerProject)</p>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<p class="eyebrow">Collaborators</p>
|
|
<p>@subscription.MaxCollaborators</p>
|
|
</div>
|
|
</div>
|
|
}
|
|
<div class="button-row mt-3">
|
|
<a class="btn btn-outline-primary" asp-action="Subscription">View subscription details</a>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="edit-panel border border-danger-subtle">
|
|
<h2 class="text-danger">Danger Zone</h2>
|
|
<p>Permanently close your PlotDirector account.</p>
|
|
<p class="muted">This will delete your account, all projects, uploaded files, notes and project data. This action cannot be undone.</p>
|
|
|
|
@if (Model.HasPaidBillingRisk)
|
|
{
|
|
<div class="alert alert-warning">
|
|
You currently have an active paid subscription.
|
|
Please cancel your subscription before closing your account. Once your account has returned to trial or free access, you can permanently delete it.
|
|
</div>
|
|
<a class="btn btn-outline-primary" asp-action="Subscription">Manage subscription</a>
|
|
}
|
|
else
|
|
{
|
|
<button class="btn btn-danger"
|
|
type="button"
|
|
data-bs-toggle="modal"
|
|
data-bs-target="#deleteAccountModal">
|
|
Delete Account
|
|
</button>
|
|
}
|
|
</section>
|
|
|
|
@if (!Model.HasPaidBillingRisk)
|
|
{
|
|
<div class="modal fade" id="deleteAccountModal" tabindex="-1" aria-labelledby="deleteAccountModalTitle" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-centered">
|
|
<div class="modal-content plotline-confirm-modal">
|
|
<div class="modal-header">
|
|
<h2 class="modal-title fs-5" id="deleteAccountModalTitle">Permanently close your account?</h2>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Cancel"></button>
|
|
</div>
|
|
<form asp-action="HardDeleteAccount" method="post" data-confirm-skip="true" data-delete-account-form>
|
|
<div class="modal-body">
|
|
<p>This will permanently delete your account and all data stored by PlotDirector, including all projects, uploaded files, notes, settings and trial information.</p>
|
|
<p class="mb-3">This action cannot be undone.</p>
|
|
<label class="form-label" for="deleteAccountConfirmationEmail">Type your email address to continue</label>
|
|
<input class="form-control"
|
|
id="deleteAccountConfirmationEmail"
|
|
name="confirmationEmail"
|
|
autocomplete="off"
|
|
data-delete-account-confirmation
|
|
data-account-email="@Model.User.Email" />
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="submit" class="btn btn-danger" disabled data-delete-account-submit>Delete Account</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
@functions {
|
|
private static string FormatLimit(int value) => value >= 999999 ? "Practical unlimited" : value.ToString("N0");
|
|
|
|
private static string FormatStorage(int value) => value >= 1024 ? $"{value / 1024:N0} GB" : $"{value:N0} MB";
|
|
|
|
private static string FormatDate(DateTime value) => value.ToLocalTime().ToString("yyyy-MM-dd");
|
|
}
|
|
|
|
@section Scripts {
|
|
@if (!Model.HasPaidBillingRisk)
|
|
{
|
|
<script>
|
|
(() => {
|
|
const modalElement = document.getElementById("deleteAccountModal");
|
|
if (!modalElement) return;
|
|
|
|
const input = modalElement.querySelector("[data-delete-account-confirmation]");
|
|
const submitButton = modalElement.querySelector("[data-delete-account-submit]");
|
|
const accountEmail = (input?.dataset.accountEmail || "").trim().toLowerCase();
|
|
|
|
const updateSubmitState = () => {
|
|
if (!input || !submitButton) return;
|
|
submitButton.disabled = input.value.trim().toLowerCase() !== accountEmail;
|
|
};
|
|
|
|
modalElement.addEventListener("show.bs.modal", () => {
|
|
if (input) input.value = "";
|
|
updateSubmitState();
|
|
});
|
|
|
|
modalElement.addEventListener("shown.bs.modal", () => input?.focus());
|
|
input?.addEventListener("input", updateSubmitState);
|
|
})();
|
|
</script>
|
|
}
|
|
}
|