mirror of
https://github.com/awatertrevi/xamarin-neo4j.git
synced 2026-09-22 09:05:29 +00:00
Design: dashboard-style ConnectionsPage with rich connection cards
- Add header with ProjectDiagram icon, app title, and connection count - Replace plain ListView with styled cards (Border + rounded corners) - Each card shows: database icon, name, host, scheme badge, secure lock - Inline edit/delete buttons on each card (pencil + trash icons) - Hide nav bar title (dashboard header replaces it) - Add computed properties: IsSecure, SchemeShortName, DisplayName - Add ConnectionCountLabel to ViewModel - Add OpenConnection command for card tap handling Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -27,6 +27,12 @@ namespace Xamarin.Neo4j.Models
|
|||||||
|
|
||||||
public string Password { get; set; }
|
public string Password { get; set; }
|
||||||
|
|
||||||
|
public bool IsSecure => Scheme?.Contains("+s") ?? false;
|
||||||
|
|
||||||
|
public string SchemeShortName => Scheme != null && Scheme.StartsWith("bolt") ? "bolt" : "neo4j";
|
||||||
|
|
||||||
|
public string DisplayName => string.IsNullOrWhiteSpace(Name) ? Host : Name;
|
||||||
|
|
||||||
public Tuple<string, bool, bool> ParseHost()
|
public Tuple<string, bool, bool> ParseHost()
|
||||||
{
|
{
|
||||||
var fullHost = Scheme + Host;
|
var fullHost = Scheme + Host;
|
||||||
|
|||||||
@@ -3,30 +3,130 @@
|
|||||||
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||||
xmlns:fonts="clr-namespace:Xamarin.Neo4j.Fonts;assembly=Xamarin.Neo4j"
|
xmlns:fonts="clr-namespace:Xamarin.Neo4j.Fonts;assembly=Xamarin.Neo4j"
|
||||||
xmlns:controls="clr-namespace:Xamarin.Neo4j.Controls;assembly=Xamarin.Neo4j"
|
|
||||||
x:Class="Xamarin.Neo4j.Pages.ConnectionsPage"
|
x:Class="Xamarin.Neo4j.Pages.ConnectionsPage"
|
||||||
x:Name="connectionsPage"
|
x:Name="connectionsPage"
|
||||||
Title="PocketGraph">
|
Title=" ">
|
||||||
|
|
||||||
<ContentPage.ToolbarItems>
|
<ContentPage.ToolbarItems>
|
||||||
<ToolbarItem Text="Settings" Order="Secondary" Command="{Binding Commands[OpenSettings]}" />
|
<ToolbarItem Text="Settings" Order="Secondary" Command="{Binding Commands[OpenSettings]}" />
|
||||||
</ContentPage.ToolbarItems>
|
</ContentPage.ToolbarItems>
|
||||||
|
|
||||||
<ContentPage.Content>
|
<ContentPage.Content>
|
||||||
<Grid>
|
<Grid RowDefinitions="Auto,*">
|
||||||
<ListView ItemsSource="{Binding ConnectionStrings}" HasUnevenRows="True" SelectionMode="None" ItemTapped="OpenSession" IsVisible="{Binding HasItems}">
|
|
||||||
<ListView.ItemTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<controls:ConnectionCell>
|
|
||||||
<controls:ConnectionCell.ContextActions>
|
|
||||||
<MenuItem Text="Edit" Command="{Binding BindingContext.Commands[EditConnectionString], Source={x:Reference connectionsPage}}" CommandParameter="{Binding .}" />
|
|
||||||
<MenuItem Text="Delete" Command="{Binding BindingContext.Commands[DeleteConnectionString], Source={x:Reference connectionsPage}}" CommandParameter="{Binding .}" IsDestructive="true" />
|
|
||||||
</controls:ConnectionCell.ContextActions>
|
|
||||||
</controls:ConnectionCell>
|
|
||||||
</DataTemplate>
|
|
||||||
</ListView.ItemTemplate>
|
|
||||||
</ListView>
|
|
||||||
|
|
||||||
|
<!-- Row 0: Dashboard header -->
|
||||||
|
<StackLayout Grid.Row="0" Spacing="4" Padding="16,16,16,12" HorizontalOptions="Center">
|
||||||
|
<Label Text="{x:Static fonts:FontAwesomeSolid.ProjectDiagram}"
|
||||||
|
FontFamily="{StaticResource FontAwesomeSolid}"
|
||||||
|
FontSize="32"
|
||||||
|
TextColor="{DynamicResource PrimaryTextColor}"
|
||||||
|
HorizontalOptions="Center"
|
||||||
|
Margin="0,0,0,4" />
|
||||||
|
<Label Text="PocketGraph"
|
||||||
|
FontSize="22" FontAttributes="Bold"
|
||||||
|
TextColor="{DynamicResource PrimaryTextColor}"
|
||||||
|
HorizontalOptions="Center" />
|
||||||
|
<Label Text="{Binding ConnectionCountLabel}"
|
||||||
|
FontSize="13"
|
||||||
|
TextColor="{DynamicResource SecondaryTextColor}"
|
||||||
|
HorizontalOptions="Center" />
|
||||||
|
</StackLayout>
|
||||||
|
|
||||||
|
<!-- Row 1: Connection cards + empty state + FAB -->
|
||||||
|
<Grid Grid.Row="1">
|
||||||
|
|
||||||
|
<!-- Scrollable connection cards -->
|
||||||
|
<ScrollView IsVisible="{Binding HasItems}">
|
||||||
|
<StackLayout BindableLayout.ItemsSource="{Binding ConnectionStrings}"
|
||||||
|
Spacing="0" Padding="0,0,0,80">
|
||||||
|
<BindableLayout.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<Border Margin="16,0,16,8"
|
||||||
|
BackgroundColor="{DynamicResource Extreme}"
|
||||||
|
Stroke="{DynamicResource QueryTextBorder}"
|
||||||
|
StrokeThickness="1"
|
||||||
|
StrokeShape="RoundRectangle 10">
|
||||||
|
<Grid ColumnDefinitions="Auto,*,Auto" Padding="14,12">
|
||||||
|
<Grid.GestureRecognizers>
|
||||||
|
<TapGestureRecognizer
|
||||||
|
Command="{Binding BindingContext.Commands[OpenConnection], Source={x:Reference connectionsPage}}"
|
||||||
|
CommandParameter="{Binding .}" />
|
||||||
|
</Grid.GestureRecognizers>
|
||||||
|
|
||||||
|
<!-- Database icon -->
|
||||||
|
<Border Grid.Column="0"
|
||||||
|
WidthRequest="40" HeightRequest="40"
|
||||||
|
StrokeThickness="0"
|
||||||
|
StrokeShape="RoundRectangle 20"
|
||||||
|
BackgroundColor="{DynamicResource QueryActionBar}"
|
||||||
|
VerticalOptions="Center">
|
||||||
|
<Label Text="{x:Static fonts:FontAwesomeSolid.Database}"
|
||||||
|
FontFamily="{StaticResource FontAwesomeSolid}"
|
||||||
|
FontSize="16"
|
||||||
|
TextColor="{DynamicResource SecondaryTextColor}"
|
||||||
|
HorizontalOptions="Center"
|
||||||
|
VerticalOptions="Center" />
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- Name + Host + scheme badge inline -->
|
||||||
|
<StackLayout Grid.Column="1" Spacing="3" Padding="12,0,8,0" VerticalOptions="Center">
|
||||||
|
<Label Text="{Binding DisplayName}"
|
||||||
|
FontSize="15" FontAttributes="Bold"
|
||||||
|
TextColor="{DynamicResource PrimaryTextColor}"
|
||||||
|
LineBreakMode="TailTruncation" MaxLines="1" />
|
||||||
|
<StackLayout Orientation="Horizontal" Spacing="6">
|
||||||
|
<Label Text="{Binding Host}"
|
||||||
|
FontSize="12"
|
||||||
|
TextColor="{DynamicResource SecondaryTextColor}"
|
||||||
|
LineBreakMode="TailTruncation"
|
||||||
|
VerticalOptions="Center" />
|
||||||
|
<Label IsVisible="{Binding IsSecure}"
|
||||||
|
Text="{x:Static fonts:FontAwesomeSolid.Lock}"
|
||||||
|
FontFamily="{StaticResource FontAwesomeSolid}"
|
||||||
|
FontSize="9"
|
||||||
|
TextColor="{DynamicResource SecondaryTextColor}"
|
||||||
|
VerticalOptions="Center" />
|
||||||
|
<Border BackgroundColor="{DynamicResource QueryActionBar}"
|
||||||
|
StrokeThickness="0"
|
||||||
|
StrokeShape="RoundRectangle 6"
|
||||||
|
Padding="6,2">
|
||||||
|
<Label Text="{Binding SchemeShortName}"
|
||||||
|
FontSize="9" FontAttributes="Bold"
|
||||||
|
TextColor="{DynamicResource SecondaryTextColor}"
|
||||||
|
VerticalOptions="Center" />
|
||||||
|
</Border>
|
||||||
|
</StackLayout>
|
||||||
|
</StackLayout>
|
||||||
|
|
||||||
|
<!-- Edit / Delete -->
|
||||||
|
<StackLayout Grid.Column="2" Orientation="Horizontal" Spacing="0" VerticalOptions="Center">
|
||||||
|
<Button Text="{x:Static fonts:FontAwesomeSolid.PencilAlt}"
|
||||||
|
FontFamily="{StaticResource FontAwesomeSolid}"
|
||||||
|
FontSize="14"
|
||||||
|
WidthRequest="38" HeightRequest="38"
|
||||||
|
BackgroundColor="Transparent"
|
||||||
|
TextColor="{DynamicResource SecondaryTextColor}"
|
||||||
|
Padding="0"
|
||||||
|
Command="{Binding BindingContext.Commands[EditConnectionString], Source={x:Reference connectionsPage}}"
|
||||||
|
CommandParameter="{Binding .}" />
|
||||||
|
<Button Text="{x:Static fonts:FontAwesomeSolid.TrashAlt}"
|
||||||
|
FontFamily="{StaticResource FontAwesomeSolid}"
|
||||||
|
FontSize="14"
|
||||||
|
WidthRequest="38" HeightRequest="38"
|
||||||
|
BackgroundColor="Transparent"
|
||||||
|
TextColor="{DynamicResource SecondaryTextColor}"
|
||||||
|
Padding="0"
|
||||||
|
Command="{Binding BindingContext.Commands[DeleteConnectionString], Source={x:Reference connectionsPage}}"
|
||||||
|
CommandParameter="{Binding .}" />
|
||||||
|
</StackLayout>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</DataTemplate>
|
||||||
|
</BindableLayout.ItemTemplate>
|
||||||
|
</StackLayout>
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
<!-- Empty state -->
|
||||||
<StackLayout IsVisible="{Binding IsEmpty}" VerticalOptions="Center" HorizontalOptions="Center" Spacing="12">
|
<StackLayout IsVisible="{Binding IsEmpty}" VerticalOptions="Center" HorizontalOptions="Center" Spacing="12">
|
||||||
<Label Text="{x:Static fonts:FontAwesomeSolid.Server}"
|
<Label Text="{x:Static fonts:FontAwesomeSolid.Server}"
|
||||||
FontFamily="{StaticResource FontAwesomeSolid}"
|
FontFamily="{StaticResource FontAwesomeSolid}"
|
||||||
@@ -50,11 +150,13 @@ x:Class="Xamarin.Neo4j.Pages.ConnectionsPage"
|
|||||||
FontSize="20"
|
FontSize="20"
|
||||||
WidthRequest="56" HeightRequest="56"
|
WidthRequest="56" HeightRequest="56"
|
||||||
CornerRadius="28"
|
CornerRadius="28"
|
||||||
BackgroundColor="{DynamicResource Accent}"
|
BackgroundColor="{DynamicResource NavigationBarColor}"
|
||||||
TextColor="White"
|
TextColor="White"
|
||||||
VerticalOptions="End" HorizontalOptions="End"
|
VerticalOptions="End" HorizontalOptions="End"
|
||||||
Margin="0,0,16,16"
|
Margin="0,0,16,16"
|
||||||
Command="{Binding Commands[AddConnection]}" />
|
Command="{Binding Commands[AddConnection]}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
</Grid>
|
||||||
</ContentPage.Content>
|
</ContentPage.Content>
|
||||||
</ContentPage>
|
</ContentPage>
|
||||||
|
|||||||
@@ -42,9 +42,7 @@ namespace Xamarin.Neo4j.Pages
|
|||||||
ViewModel.LoadConnectionStrings();
|
ViewModel.LoadConnectionStrings();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OpenSession(object sender, ItemTappedEventArgs e)
|
|
||||||
{
|
|
||||||
ViewModel.OpenSession((Neo4jConnectionString)e.Item);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,6 +50,12 @@ namespace Xamarin.Neo4j.ViewModels
|
|||||||
LoadConnectionStrings();
|
LoadConnectionStrings();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
Commands.Add("OpenConnection", new Command((o) =>
|
||||||
|
{
|
||||||
|
if (o is Neo4jConnectionString cs)
|
||||||
|
OpenSession(cs);
|
||||||
|
}));
|
||||||
|
|
||||||
Commands.Add("OpenSettings", new Command(async () =>
|
Commands.Add("OpenSettings", new Command(async () =>
|
||||||
{
|
{
|
||||||
await Navigation.PushAsync(new SettingsPage());
|
await Navigation.PushAsync(new SettingsPage());
|
||||||
@@ -87,6 +93,7 @@ namespace Xamarin.Neo4j.ViewModels
|
|||||||
OnPropertyChanged(nameof(ConnectionStrings));
|
OnPropertyChanged(nameof(ConnectionStrings));
|
||||||
OnPropertyChanged(nameof(IsEmpty));
|
OnPropertyChanged(nameof(IsEmpty));
|
||||||
OnPropertyChanged(nameof(HasItems));
|
OnPropertyChanged(nameof(HasItems));
|
||||||
|
OnPropertyChanged(nameof(ConnectionCountLabel));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,6 +101,20 @@ namespace Xamarin.Neo4j.ViewModels
|
|||||||
|
|
||||||
public bool HasItems => !IsEmpty;
|
public bool HasItems => !IsEmpty;
|
||||||
|
|
||||||
|
public string ConnectionCountLabel
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var count = _connectionStrings?.Count() ?? 0;
|
||||||
|
return count switch
|
||||||
|
{
|
||||||
|
0 => "No connections yet",
|
||||||
|
1 => "1 connection",
|
||||||
|
_ => $"{count} connections"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user