Files
aliasvault/apps/server/AliasVault.Client/Main/Pages/Welcome.razor
2025-12-24 10:30:27 +01:00

222 lines
11 KiB
Plaintext

@page "/welcome"
@inherits MainBase
@using AliasVault.Shared.Core.BrowserExtensions
@using AliasVault.Shared.Core.MobileApps
@using Microsoft.Extensions.Localization
<div class="bg-gray-100 dark:bg-gray-900 flex flex-col lg:items-center lg:justify-center">
<div class="w-full mt-4 lg:mt-16 mx-auto lg:max-w-4xl lg:bg-white lg:dark:bg-gray-800 lg:shadow-xl lg:rounded-lg lg:overflow-hidden flex flex-col">
<div class="flex flex-col flex-grow">
<div class="flex-grow p-6 pt-4 lg:pt-6 lg:pb-4">
<div class="flex justify-between items-center mb-4">
<div class="flex-grow text-center">
<h2 class="text-xl font-semibold text-gray-900 dark:text-white">@GetStepTitle(_currentStep)</h2>
</div>
</div>
@if (GetProgressPercentage() > 0)
{
<div class="w-full bg-gray-200 rounded-full h-2.5 mb-4 dark:bg-gray-700 mt-4">
<div class="bg-primary-600 h-2.5 rounded-full" style="width: @(GetProgressPercentage())%"></div>
</div>
}
@switch (_currentStep)
{
case TutorialStep.Welcome:
<div class="space-y-4">
<p class="text-gray-600 dark:text-gray-400">
@Localizer["WelcomeMessage"]
</p>
</div>
break;
case TutorialStep.HowAliasVaultWorks:
<div class="space-y-4">
<p class="text-gray-600 dark:text-gray-400">
@Localizer["HowItWorksIntro"]
</p>
<ol class="list-decimal list-inside space-y-2 text-gray-600 dark:text-gray-400">
<li>@Localizer["HowItWorksStep1"]</li>
<li>@Localizer["HowItWorksStep2"]</li>
<li>@Localizer["HowItWorksStep3"]</li>
<li>@Localizer["HowItWorksStep4"]</li>
</ol>
</div>
break;
case TutorialStep.Tips:
<div class="space-y-4">
<div class="space-y-3">
<div class="p-4 bg-gray-50 dark:bg-gray-700 rounded-lg">
<h4 class="font-semibold text-gray-900 dark:text-white">@Localizer["MasterPasswordTipTitle"]</h4>
<p class="text-gray-600 dark:text-gray-400">
@Localizer["MasterPasswordTipContent"]
</p>
</div>
<div class="p-4 bg-gray-50 dark:bg-gray-700 rounded-lg">
<h4 class="font-semibold text-gray-900 dark:text-white">@Localizer["TwoFactorTipTitle"]</h4>
<p class="text-gray-600 dark:text-gray-400">@Localizer["TwoFactorTipContent"]</p>
</div>
<div class="p-4 bg-gray-50 dark:bg-gray-700 rounded-lg">
<h4 class="font-semibold text-gray-900 dark:text-white">@Localizer["ExtensionsAppsTipTitle"]</h4>
<p class="text-gray-600 dark:text-gray-400 mb-4">@Localizer["ExtensionsAppsTipContent"]</p>
<div class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-5 gap-3">
@foreach (var extension in AliasVault.Shared.Core.BrowserExtensions.Constants.Extensions.Where(x => x.Key != BrowserType.Unknown).Select(x => x.Value))
{
@if (extension.IsAvailable)
{
<a href="@extension.DownloadUrl"
target="_blank"
class="flex flex-col items-center p-3 rounded-lg bg-gray-200 dark:bg-gray-600 hover:bg-gray-300 dark:hover:bg-gray-500 transition-colors">
<img src="@extension.IconPath" alt="@extension.Name" class="w-8 h-8 mb-2">
<span class="text-xs text-primary-600 hover:text-primary-700 dark:text-primary-400 dark:hover:text-primary-300">
@extension.Name
</span>
</a>
}
else
{
<div class="flex flex-col items-center p-3 rounded-lg bg-gray-200 dark:bg-gray-600">
<img src="@extension.IconPath" alt="@extension.Name" class="w-8 h-8 mb-2 opacity-50">
<span class="text-xs text-gray-500 dark:text-gray-400">
@Localizer["ComingSoonLabel"]
</span>
</div>
}
}
@foreach (var app in AliasVault.Shared.Core.MobileApps.Constants.MobileApps)
{
@if (app.IsAvailable)
{
<a href="@app.DownloadUrl"
target="_blank"
class="flex flex-col items-center p-3 rounded-lg bg-gray-200 dark:bg-gray-600 hover:bg-gray-300 dark:hover:bg-gray-500 transition-colors">
<img src="@app.IconPath" alt="@app.Name" class="w-8 h-8 mb-2">
<span class="text-xs text-primary-600 hover:text-primary-700 dark:text-primary-400 dark:hover:text-primary-300">
@app.Name
</span>
</a>
}
else
{
<div class="flex flex-col items-center p-3 rounded-lg bg-gray-200 dark:bg-gray-600">
<img src="@app.IconPath" alt="@app.Name" class="w-8 h-8 mb-2 opacity-50">
<span class="text-xs text-gray-500 dark:text-gray-400">
@app.Name @Localizer["SoonSuffix"]
</span>
</div>
}
}
</div>
</div>
</div>
</div>
break;
case TutorialStep.CreateFirstIdentity:
<div class="space-y-4">
<h3 class="text-2xl font-bold text-gray-900 dark:text-white">@Localizer["ReadyToStartTitle"]</h3>
<p class="text-gray-600 dark:text-gray-400">
@Localizer["ReadyToStartMessage"]
</p>
<div class="mt-4">
<button @onclick="CreateFirstIdentity"
class="w-full bg-primary-600 hover:bg-primary-700 text-white font-semibold py-3 px-4 rounded-lg transition duration-300">
@Localizer["CreateFirstIdentityButton"]
</button>
</div>
</div>
break;
}
</div>
<div class="fixed lg:relative bottom-0 left-0 right-0 p-4 bg-gray-100 dark:bg-gray-900 border-t border-gray-200 dark:border-gray-700 lg:bg-transparent lg:dark:bg-transparent lg:border-0">
@if (_currentStep != TutorialStep.Tips)
{
<button @onclick="GoNext"
class="w-full py-3 px-4 bg-primary-600 hover:bg-primary-700 text-white font-semibold rounded-lg transition duration-300">
@Localizer["ContinueButton"]
</button>
}
else
{
<button @onclick="FinishTutorial"
class="w-full py-3 px-4 bg-green-600 hover:bg-green-700 text-white font-semibold rounded-lg transition duration-300">
@Localizer["GetStartedButton"]
</button>
}
</div>
</div>
</div>
</div>
@code {
private IStringLocalizer Localizer => LocalizerFactory.Create("Pages.Main.Welcome", "AliasVault.Client");
private TutorialStep _currentStep = TutorialStep.Welcome;
private enum TutorialStep
{
Welcome,
HowAliasVaultWorks,
Tips,
CreateFirstIdentity
}
private string GetStepTitle(TutorialStep step)
{
return step switch
{
TutorialStep.Welcome => Localizer["WelcomeStepTitle"],
TutorialStep.HowAliasVaultWorks => Localizer["HowAliasVaultWorksStepTitle"],
TutorialStep.Tips => Localizer["TipsStepTitle"],
TutorialStep.CreateFirstIdentity => Localizer["GetStartedStepTitle"],
_ => Localizer["TutorialStepTitle"]
};
}
/// <inheritdoc />
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
// If tutorial is already done, redirect to the home page.
if (DbService.Settings.TutorialDone)
{
NavigationManager.NavigateTo("/");
}
}
private async Task GoNext()
{
_currentStep = _currentStep switch
{
TutorialStep.Welcome => TutorialStep.HowAliasVaultWorks,
TutorialStep.HowAliasVaultWorks => TutorialStep.Tips,
TutorialStep.Tips => TutorialStep.CreateFirstIdentity,
_ => _currentStep
};
await JsInteropService.ScrollToTop();
}
private void CreateFirstIdentity()
{
NavigationManager.NavigateTo("items/create");
}
private async Task FinishTutorial()
{
GlobalLoadingSpinner.Show(Localizer["FinishingTutorialMessage"]);
await DbService.Settings.SetTutorialDoneAsync(true);
NavigationManager.NavigateTo("items");
GlobalLoadingSpinner.Hide();
}
private int GetProgressPercentage()
{
return (int)_currentStep * 100 / (Enum.GetValues(typeof(TutorialStep)).Length - 1);
}
}