b1f8e3e50b
New workflow alongside "תייק ל-Klear": save just the attachments of a
mail to a Case in EspoCRM (no Email entity created), with an optional
DocumentFolder.
Core:
- AttachmentEntity, DocumentEntity, DocumentFolder DTOs
- IEspoCrmClient gets three new endpoints:
UploadAttachmentAsync (POST /Attachment, data-URI base64, role=Attachment)
CreateDocumentAsync (POST /Document, fileId + parent + folderId)
ListDocumentFoldersAsync (GET /DocumentFolder, returns envelope)
- AttachmentSaveService orchestrates per-attachment upload + Document
create, short-circuits on EspoCrmAuthorizationException, aggregates
per-file Saved/Failed/AuthRequired outcomes into AttachmentSaveSummary
UI:
- SaveAttachmentsDialog (RTL, card layout matching FileToCaseDialog):
Case search + folder dropdown + per-attachment checkbox list
- SaveAttachmentsViewModel: debounced GlobalSearch filtered to Case,
optional folder selector (with a "(no folder)" sentinel row),
Attachments collection of AttachmentRowViewModel rows
- AttachmentRowViewModel: IsSelected checkbox + display name + size
computed from base64 length
Host:
- AddInHost.LaunchSaveAttachmentsAsync(MailItem) — extracts on the STA
via MailItemExtractor, fetches folders, shows the dialog (anchored to
Outlook via AttachOwnerToOutlook), runs AttachmentSaveService on OK,
reports a Hebrew MessageBox summary
- AttachmentSaveService field on AddInHost + dispose hook
Ribbon:
- New "שמור קבצים" button in the Marcus-Law group, between Klear and
פרטי תיק. Enabled only when exactly one MailItem is selected and it
has at least one attachment
- klear-attach.png paperclip icon (32x32, blue rounded square),
embedded as a resource and resolved by the existing OnGetImage
callback (new case in the switch)
Tests: 39 still passing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
216 lines
11 KiB
XML
216 lines
11 KiB
XML
<Window x:Class="MarcusLaw.OutlookAddin.UI.Dialogs.SaveAttachmentsDialog"
|
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
Title="שמור קבצים מצורפים ל-Klear"
|
|
Height="560" Width="620"
|
|
MinHeight="420" MinWidth="500"
|
|
FlowDirection="RightToLeft"
|
|
WindowStartupLocation="CenterOwner"
|
|
ShowInTaskbar="False"
|
|
ResizeMode="CanResize"
|
|
Background="#F8FAFC"
|
|
FontFamily="Segoe UI, David, Frank Ruehl, Arial">
|
|
<Window.Resources>
|
|
<SolidColorBrush x:Key="Primary" Color="#2563EB" />
|
|
<SolidColorBrush x:Key="PrimaryHover" Color="#1D4ED8" />
|
|
<SolidColorBrush x:Key="Border" Color="#E2E8F0" />
|
|
<SolidColorBrush x:Key="Muted" Color="#64748B" />
|
|
<SolidColorBrush x:Key="CardBg" Color="White" />
|
|
<SolidColorBrush x:Key="HoverBg" Color="#F1F5F9" />
|
|
<SolidColorBrush x:Key="SelectedBg" Color="#DBEAFE" />
|
|
|
|
<Style x:Key="Card" TargetType="Border">
|
|
<Setter Property="Background" Value="{StaticResource CardBg}" />
|
|
<Setter Property="BorderBrush" Value="{StaticResource Border}" />
|
|
<Setter Property="BorderThickness" Value="1" />
|
|
<Setter Property="CornerRadius" Value="6" />
|
|
</Style>
|
|
|
|
<Style x:Key="PrimaryButton" TargetType="Button">
|
|
<Setter Property="Background" Value="{StaticResource Primary}" />
|
|
<Setter Property="Foreground" Value="White" />
|
|
<Setter Property="BorderThickness" Value="0" />
|
|
<Setter Property="Padding" Value="22,8" />
|
|
<Setter Property="FontWeight" Value="SemiBold" />
|
|
<Setter Property="MinWidth" Value="110" />
|
|
<Setter Property="Cursor" Value="Hand" />
|
|
<Setter Property="Template">
|
|
<Setter.Value>
|
|
<ControlTemplate TargetType="Button">
|
|
<Border x:Name="Bd" Background="{TemplateBinding Background}" CornerRadius="5" Padding="{TemplateBinding Padding}">
|
|
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
|
|
</Border>
|
|
<ControlTemplate.Triggers>
|
|
<Trigger Property="IsMouseOver" Value="True">
|
|
<Setter TargetName="Bd" Property="Background" Value="{StaticResource PrimaryHover}" />
|
|
</Trigger>
|
|
<Trigger Property="IsEnabled" Value="False">
|
|
<Setter TargetName="Bd" Property="Background" Value="#94A3B8" />
|
|
</Trigger>
|
|
</ControlTemplate.Triggers>
|
|
</ControlTemplate>
|
|
</Setter.Value>
|
|
</Setter>
|
|
</Style>
|
|
|
|
<Style x:Key="SecondaryButton" TargetType="Button">
|
|
<Setter Property="Background" Value="White" />
|
|
<Setter Property="Foreground" Value="#0F172A" />
|
|
<Setter Property="BorderBrush" Value="{StaticResource Border}" />
|
|
<Setter Property="BorderThickness" Value="1" />
|
|
<Setter Property="Padding" Value="20,8" />
|
|
<Setter Property="MinWidth" Value="110" />
|
|
<Setter Property="Cursor" Value="Hand" />
|
|
<Setter Property="Template">
|
|
<Setter.Value>
|
|
<ControlTemplate TargetType="Button">
|
|
<Border x:Name="Bd" Background="{TemplateBinding Background}"
|
|
BorderBrush="{TemplateBinding BorderBrush}"
|
|
BorderThickness="{TemplateBinding BorderThickness}"
|
|
CornerRadius="5" Padding="{TemplateBinding Padding}">
|
|
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
|
|
</Border>
|
|
<ControlTemplate.Triggers>
|
|
<Trigger Property="IsMouseOver" Value="True">
|
|
<Setter TargetName="Bd" Property="Background" Value="{StaticResource HoverBg}" />
|
|
</Trigger>
|
|
</ControlTemplate.Triggers>
|
|
</ControlTemplate>
|
|
</Setter.Value>
|
|
</Setter>
|
|
</Style>
|
|
|
|
<Style x:Key="CaseItem" TargetType="ListBoxItem">
|
|
<Setter Property="Padding" Value="0" />
|
|
<Setter Property="Margin" Value="0" />
|
|
<Setter Property="Background" Value="Transparent" />
|
|
<Setter Property="Template">
|
|
<Setter.Value>
|
|
<ControlTemplate TargetType="ListBoxItem">
|
|
<Border x:Name="Bd" Background="{TemplateBinding Background}" CornerRadius="4"
|
|
Margin="2,1" Padding="8,6">
|
|
<ContentPresenter />
|
|
</Border>
|
|
<ControlTemplate.Triggers>
|
|
<Trigger Property="IsMouseOver" Value="True">
|
|
<Setter TargetName="Bd" Property="Background" Value="{StaticResource HoverBg}" />
|
|
</Trigger>
|
|
<Trigger Property="IsSelected" Value="True">
|
|
<Setter TargetName="Bd" Property="Background" Value="{StaticResource SelectedBg}" />
|
|
</Trigger>
|
|
</ControlTemplate.Triggers>
|
|
</ControlTemplate>
|
|
</Setter.Value>
|
|
</Setter>
|
|
</Style>
|
|
</Window.Resources>
|
|
|
|
<Grid Margin="16">
|
|
<Grid.RowDefinitions>
|
|
<RowDefinition Height="Auto" />
|
|
<RowDefinition Height="*" />
|
|
<RowDefinition Height="Auto" />
|
|
<RowDefinition Height="Auto" />
|
|
<RowDefinition Height="Auto" />
|
|
<RowDefinition Height="Auto" />
|
|
</Grid.RowDefinitions>
|
|
|
|
<!-- Search -->
|
|
<Border Grid.Row="0" Style="{StaticResource Card}" Padding="10,8" Margin="0,0,0,8">
|
|
<DockPanel LastChildFill="True">
|
|
<TextBlock DockPanel.Dock="Left" Text="🔍" VerticalAlignment="Center" FontSize="16" Margin="0,0,8,0" />
|
|
<TextBox x:Name="SearchBox"
|
|
Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged}"
|
|
BorderThickness="0"
|
|
Background="Transparent"
|
|
FontSize="14"
|
|
VerticalAlignment="Center" />
|
|
</DockPanel>
|
|
</Border>
|
|
|
|
<!-- Cases -->
|
|
<Border Grid.Row="1" Style="{StaticResource Card}">
|
|
<ListBox ItemsSource="{Binding Cases}"
|
|
SelectedItem="{Binding SelectedCase}"
|
|
BorderThickness="0"
|
|
Background="Transparent"
|
|
ItemContainerStyle="{StaticResource CaseItem}"
|
|
Padding="4">
|
|
<ListBox.ItemTemplate>
|
|
<DataTemplate>
|
|
<StackPanel>
|
|
<TextBlock FontSize="13">
|
|
<Run Text="[" /><Run Text="{Binding Number, Mode=OneWay}" /><Run Text="] " />
|
|
<Run Text="{Binding Name, Mode=OneWay}" FontWeight="SemiBold" />
|
|
</TextBlock>
|
|
</StackPanel>
|
|
</DataTemplate>
|
|
</ListBox.ItemTemplate>
|
|
</ListBox>
|
|
</Border>
|
|
|
|
<!-- Folder -->
|
|
<DockPanel Grid.Row="2" Margin="0,10,0,0" LastChildFill="True">
|
|
<TextBlock DockPanel.Dock="Left" Text="תיקייה:" VerticalAlignment="Center"
|
|
Margin="0,0,8,0" Foreground="{StaticResource Muted}" FontSize="12" />
|
|
<ComboBox ItemsSource="{Binding Folders}"
|
|
SelectedItem="{Binding SelectedFolder}"
|
|
DisplayMemberPath="Name"
|
|
Padding="6,4" />
|
|
</DockPanel>
|
|
|
|
<!-- Attachments header -->
|
|
<TextBlock Grid.Row="3" Margin="0,12,0,4" Foreground="{StaticResource Muted}" FontSize="12">
|
|
<Run Text="קבצים מצורפים (" /><Run Text="{Binding Attachments.Count, Mode=OneWay}" /><Run Text=")" />
|
|
</TextBlock>
|
|
|
|
<!-- Attachments list -->
|
|
<Border Grid.Row="4" Style="{StaticResource Card}" MaxHeight="160">
|
|
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
|
<ItemsControl ItemsSource="{Binding Attachments}" Margin="6">
|
|
<ItemsControl.ItemTemplate>
|
|
<DataTemplate>
|
|
<DockPanel Margin="0,3" LastChildFill="True">
|
|
<CheckBox DockPanel.Dock="Right" IsChecked="{Binding IsSelected, Mode=TwoWay}"
|
|
VerticalAlignment="Center" />
|
|
<TextBlock Text="{Binding SizeDisplay}"
|
|
DockPanel.Dock="Left"
|
|
Foreground="{StaticResource Muted}"
|
|
FontSize="11"
|
|
VerticalAlignment="Center"
|
|
Margin="8,0,0,0" />
|
|
<TextBlock Text="{Binding FileName}"
|
|
VerticalAlignment="Center"
|
|
TextTrimming="CharacterEllipsis"
|
|
Margin="8,0,0,0" />
|
|
</DockPanel>
|
|
</DataTemplate>
|
|
</ItemsControl.ItemTemplate>
|
|
</ItemsControl>
|
|
</ScrollViewer>
|
|
</Border>
|
|
|
|
<!-- Action row -->
|
|
<DockPanel Grid.Row="5" Margin="0,12,0,0" LastChildFill="True">
|
|
<TextBlock DockPanel.Dock="Right"
|
|
Text="{Binding StatusMessage}"
|
|
VerticalAlignment="Center"
|
|
Margin="0,0,8,0"
|
|
Foreground="{StaticResource Muted}"
|
|
FontSize="12"
|
|
TextTrimming="CharacterEllipsis" />
|
|
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
|
|
<Button Command="{Binding SubmitCommand}"
|
|
IsDefault="True"
|
|
Content="שמור"
|
|
Style="{StaticResource PrimaryButton}"
|
|
Margin="0,0,8,0" />
|
|
<Button Command="{Binding CancelCommand}"
|
|
IsCancel="True"
|
|
Content="ביטול"
|
|
Style="{StaticResource SecondaryButton}" />
|
|
</StackPanel>
|
|
</DockPanel>
|
|
</Grid>
|
|
</Window>
|