Enhance the landing page and include the recent projects for faster opening
Some checks failed
Build / build (push) Has been cancelled

This commit is contained in:
Ankitkumar Satapara
2026-04-22 14:21:30 +05:30
parent f70e21c40e
commit f9118a563c
4 changed files with 325 additions and 15 deletions

View File

@@ -0,0 +1,12 @@
using System;
namespace Nodify.Calculator.Models
{
public class RecentProjectEntry
{
public int Id { get; set; }
public string ProjectName { get; set; } = string.Empty;
public string ProjectPath { get; set; } = string.Empty;
public DateTime LastOpened { get; set; } = DateTime.Now;
}
}

View File

@@ -0,0 +1,74 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using LiteDB;
using Nodify.Calculator.Models;
namespace Nodify.Calculator
{
/// <summary>
/// Manages a per-user database of recently opened projects.
/// The DB lives in %APPDATA%/APIVisualExecutor/recentprojects.db
/// </summary>
public static class RecentProjectsStore
{
private static string DbPath
{
get
{
var appData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
var dir = Path.Combine(appData, "APIVisualExecutor");
Directory.CreateDirectory(dir);
return Path.Combine(dir, "recentprojects.db");
}
}
/// <summary>
/// Returns all recent projects ordered by last-opened descending.
/// </summary>
public static List<RecentProjectEntry> GetAll()
{
using var db = new LiteDatabase(DbPath);
var col = db.GetCollection<RecentProjectEntry>("projects");
return col.FindAll().OrderByDescending(p => p.LastOpened).ToList();
}
/// <summary>
/// Adds or updates a project entry (matched by path).
/// </summary>
public static void Upsert(string projectName, string projectFilePath)
{
using var db = new LiteDatabase(DbPath);
var col = db.GetCollection<RecentProjectEntry>("projects");
var normalized = Path.GetFullPath(projectFilePath);
var existing = col.FindOne(p => p.ProjectPath == normalized);
if (existing != null)
{
existing.ProjectName = projectName;
existing.LastOpened = DateTime.Now;
col.Update(existing);
}
else
{
col.Insert(new RecentProjectEntry
{
ProjectName = projectName,
ProjectPath = normalized,
LastOpened = DateTime.Now
});
}
}
/// <summary>
/// Removes a project entry by path.
/// </summary>
public static void Remove(string projectFilePath)
{
using var db = new LiteDatabase(DbPath);
var col = db.GetCollection<RecentProjectEntry>("projects");
var normalized = Path.GetFullPath(projectFilePath);
col.DeleteMany(p => p.ProjectPath == normalized);
}
}
}

View File

@@ -2,38 +2,195 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="API Visual Executor"
Width="500"
Height="380"
Width="720"
Height="520"
WindowStartupLocation="CenterScreen"
ResizeMode="NoResize"
ResizeMode="CanResizeWithGrip"
Background="#1E1E1E"
Foreground="White">
<Grid>
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Width="360">
<Grid Margin="32,24">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!-- Header -->
<StackPanel Grid.Row="0" HorizontalAlignment="Center" Margin="0,0,0,6">
<TextBlock Text="API Visual Executor"
FontSize="26" FontWeight="Bold"
FontSize="28" FontWeight="Bold"
Foreground="#6366f1"
HorizontalAlignment="Center"
Margin="0 0 0 8" />
HorizontalAlignment="Center" />
<TextBlock Text="Create or open a project to get started"
FontSize="13" Foreground="Gray"
HorizontalAlignment="Center"
Margin="0 0 0 30" />
Margin="0,4,0,0" />
</StackPanel>
<!-- Action buttons -->
<StackPanel Grid.Row="1" Orientation="Horizontal"
HorizontalAlignment="Center" Margin="0,14,0,14">
<Button Content=" New Project"
Height="48" FontSize="16"
Margin="0 0 0 14"
Width="200" Height="42" FontSize="15"
Margin="0,0,12,0"
Background="#6366f1" Foreground="White"
BorderThickness="0" Cursor="Hand"
Click="NewProject_Click" />
<Button Content="📂 Open Project"
Height="48" FontSize="16"
Margin="0 0 0 14"
Width="200" Height="42" FontSize="15"
Background="#3E3E42" Foreground="White"
BorderThickness="1" BorderBrush="#555"
Cursor="Hand"
Click="OpenProject_Click" />
</StackPanel>
<!-- Recent projects grid -->
<Border Grid.Row="2" Background="#252526" CornerRadius="6"
BorderBrush="#3E3E42" BorderThickness="1"
Padding="0">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="Recent Projects"
FontSize="14" FontWeight="SemiBold"
Foreground="#DCDCDC" Margin="14,10,14,6" />
<!-- Empty state -->
<TextBlock Grid.Row="1" x:Name="EmptyLabel"
Text="No recent projects yet. Create or open a project to see it here."
Foreground="#777" FontSize="12"
HorizontalAlignment="Center" VerticalAlignment="Center"
Visibility="Collapsed" />
<DataGrid Grid.Row="1" x:Name="RecentProjectsGrid"
AutoGenerateColumns="False"
IsReadOnly="True"
CanUserAddRows="False"
CanUserDeleteRows="False"
CanUserReorderColumns="False"
SelectionMode="Single"
HeadersVisibility="Column"
GridLinesVisibility="Horizontal"
HorizontalGridLinesBrush="#2D2D30"
Background="Transparent"
RowBackground="#252526"
AlternatingRowBackground="#2A2A2E"
BorderThickness="0"
Foreground="#DCDCDC"
FontSize="13"
Margin="6,0,6,6"
MouseDoubleClick="RecentProjectsGrid_MouseDoubleClick">
<DataGrid.ColumnHeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="Background" Value="#2D2D30" />
<Setter Property="Foreground" Value="#AAAAAA" />
<Setter Property="FontSize" Value="12" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="Padding" Value="10,6" />
<Setter Property="BorderBrush" Value="#3E3E42" />
<Setter Property="BorderThickness" Value="0,0,0,1" />
</Style>
</DataGrid.ColumnHeaderStyle>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="Cursor" Value="Hand" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#333337" />
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="#3E3E42" />
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Padding" Value="10,6" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="DataGridCell">
<Border Padding="{TemplateBinding Padding}" Background="{TemplateBinding Background}">
<ContentPresenter VerticalAlignment="Center" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="White" />
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.CellStyle>
<DataGrid.Columns>
<DataGridTextColumn Header="Project Name" Binding="{Binding ProjectName}" Width="*">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="#E0E0E0" />
<Setter Property="FontWeight" Value="Medium" />
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Location" Binding="{Binding ProjectPath}" Width="2*">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="#999999" />
<Setter Property="TextTrimming" Value="CharacterEllipsis" />
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Last Opened" Binding="{Binding LastOpened, StringFormat='{}{0:MMM dd, yyyy hh:mm tt}'}" Width="160">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="#888888" />
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<DataGridTemplateColumn Header="" Width="70">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content="Open"
Click="OpenRecentProject_Click"
Background="#6366f1" Foreground="White"
BorderThickness="0" Cursor="Hand"
Padding="10,4" FontSize="12"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="" Width="36">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content="✕"
Click="RemoveRecentProject_Click"
Background="Transparent" Foreground="#888"
BorderThickness="0" Cursor="Hand"
Padding="4,2" FontSize="12"
ToolTip="Remove from list"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Border>
<!-- Footer hint -->
<TextBlock Grid.Row="3" Text="Double-click a project to open it quickly"
FontSize="11" Foreground="#555"
HorizontalAlignment="Center" Margin="0,8,0,0" />
</Grid>
</Window>

View File

@@ -1,14 +1,32 @@
using Microsoft.Win32;
using Nodify.Calculator.Models;
using System.Collections.ObjectModel;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace Nodify.Calculator
{
public partial class StartupWindow : Window
{
public ObservableCollection<RecentProjectEntry> RecentProjects { get; } = new();
public StartupWindow()
{
InitializeComponent();
LoadRecentProjects();
}
private void LoadRecentProjects()
{
RecentProjects.Clear();
foreach (var entry in RecentProjectsStore.GetAll())
RecentProjects.Add(entry);
RecentProjectsGrid.ItemsSource = RecentProjects;
EmptyLabel.Visibility = RecentProjects.Count == 0 ? Visibility.Visible : Visibility.Collapsed;
}
private void NewProject_Click(object sender, RoutedEventArgs e)
@@ -19,7 +37,7 @@ namespace Nodify.Calculator
return;
ProjectManager.CreateProject(dialog.ProjectName, dialog.ProjectDirectory);
OpenMainWindow();
TrackAndOpen();
}
private void OpenProject_Click(object sender, RoutedEventArgs e)
@@ -35,6 +53,55 @@ namespace Nodify.Calculator
return;
ProjectManager.OpenProject(ofd.FileName);
TrackAndOpen();
}
private void OpenRecentProject_Click(object sender, RoutedEventArgs e)
{
if (sender is Button btn && btn.DataContext is RecentProjectEntry entry)
OpenRecentEntry(entry);
}
private void RemoveRecentProject_Click(object sender, RoutedEventArgs e)
{
if (sender is Button btn && btn.DataContext is RecentProjectEntry entry)
{
RecentProjectsStore.Remove(entry.ProjectPath);
LoadRecentProjects();
}
}
private void RecentProjectsGrid_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
if (RecentProjectsGrid.SelectedItem is RecentProjectEntry entry)
OpenRecentEntry(entry);
}
private void OpenRecentEntry(RecentProjectEntry entry)
{
if (!File.Exists(entry.ProjectPath))
{
var result = MessageBox.Show(
$"Project file not found:\n{entry.ProjectPath}\n\nRemove it from the list?",
"Project Not Found",
MessageBoxButton.YesNo,
MessageBoxImage.Warning);
if (result == MessageBoxResult.Yes)
{
RecentProjectsStore.Remove(entry.ProjectPath);
LoadRecentProjects();
}
return;
}
ProjectManager.OpenProject(entry.ProjectPath);
TrackAndOpen();
}
private void TrackAndOpen()
{
RecentProjectsStore.Upsert(ProjectManager.ProjectName, ProjectManager.ProjectFilePath);
OpenMainWindow();
}