Skip to content

Commit a0f69ea

Browse files
authored
Merge pull request #690 from LeavittSoftware/email-viewer
Email Viewer and snackbar snack bugs
2 parents 6df6c28 + e311a96 commit a0f69ea

11 files changed

Lines changed: 822 additions & 70 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"packages/*"
88
],
99
"dependencies": {
10-
"@leavittsoftware/lg-core-typescript": "^5.1102.0",
10+
"@leavittsoftware/lg-core-typescript": "^5.1106.0",
1111
"@material/web": "^2.4.1",
1212
"api-viewer-element": "^1.0.0-pre.10",
1313
"lit": "3.3.1",

packages/leavittbook/src/demos/leavitt-email-history-viewer/leavitt-email-history-viewer-playground.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,18 @@ import LeavittEmailHistoryViewer from '@leavittsoftware/web/leavitt/email-histor
1212

1313
/* playground-fold-end */
1414
import '@leavittsoftware/web/leavitt/email-history-viewer/email-history-viewer';
15+
import '@leavittsoftware/web/leavitt/email-history-viewer/email-history-viewer-filled';
16+
1517
import { ThemePreference } from '@leavittsoftware/web/leavitt/theme/theme-preference';
18+
import LeavittEmailHistoryViewerFilled from '@leavittsoftware/web/leavitt/email-history-viewer/email-history-viewer-filled';
1619

1720
/* playground-fold */
1821
@customElement('leavitt-email-history-viewer-playground')
1922
export class LeavittEmailHistoryViewerPlayground extends ThemePreference(LitElement) {
2023
@state() private accessor apiService: ApiService;
2124
@query('leavitt-email-history-viewer') protected accessor demo1!: LeavittEmailHistoryViewer;
22-
25+
@query('leavitt-email-history-viewer-filled') protected accessor demo2!: LeavittEmailHistoryViewerFilled;
26+
2327
constructor() {
2428
super();
2529
this.apiService = new ApiService(new AuthenticatedTokenProvider());
@@ -60,7 +64,13 @@ export class LeavittEmailHistoryViewerPlayground extends ThemePreference(LitElem
6064
/* playground-fold-end */
6165
return html`
6266
<user-manager disableAutoload></user-manager>
63-
<h1>Demo</h1>
67+
68+
<h1>Filled</h1>
69+
<main row>
70+
<leavitt-email-history-viewer-filled isActive .apiService=${this.apiService} .path=${'/leavitt-email-history-viewer'}></leavitt-email-history-viewer-filled>
71+
</main>
72+
73+
<h1>Outlined</h1>
6474
<main row>
6575
<leavitt-email-history-viewer isActive .apiService=${this.apiService} .path=${'/leavitt-email-history-viewer'}></leavitt-email-history-viewer>
6676
</main>
Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
import '../../titanium/date-range-selector/date-range-selector';
2+
3+
import '@material/web/dialog/dialog';
4+
import '@material/web/button/text-button';
5+
import '@material/web/button/filled-tonal-button';
6+
import '@material/web/icon/icon';
7+
import '@material/web/select/select-option';
8+
import '@material/web/chips/input-chip';
9+
import '@material/web/select/filled-select';
10+
11+
import { dialogZIndexHack } from '../../titanium/hacks/dialog-zindex-hack';
12+
import { LitElement, PropertyValues, css, html } from 'lit';
13+
import { customElement, property, query, state } from 'lit/decorators.js';
14+
import { MdDialog } from '@material/web/dialog/dialog';
15+
import { LoadWhile } from '../../titanium/helpers/load-while';
16+
import { FilterController } from '../../titanium/data-table/filter-controller';
17+
import { rangeLabel } from '../../titanium/date-range-selector/types/range-label';
18+
import { TitaniumDateRangeSelector } from '../../titanium/date-range-selector/date-range-selector';
19+
import { DateRangeKey } from '../../titanium/date-range-selector/types/date-range-key';
20+
import { DateRanges } from '../../titanium/date-range-selector/types/date-ranges';
21+
import { DOMEvent } from '../../titanium/types/dom-event';
22+
import { dialogCloseNavigationHack, dialogOpenNavigationHack } from '../../titanium/hacks/dialog-navigation-hack';
23+
import ApiService from '../api-service/api-service';
24+
import { EmailTemplate } from '@leavittsoftware/lg-core-typescript';
25+
import { ShowSnackbarEvent } from '../../titanium/snackbar/show-snackbar-event';
26+
import { MdOutlinedSelect } from '@material/web/select/outlined-select';
27+
28+
export type FilterKeys = 'template' | 'startDate' | 'endDate' | 'dateRange';
29+
30+
@customElement('leavitt-email-history-viewer-filled-filter-dialog')
31+
export class LeavittEmailHistoryViewerFilledFilterDialog extends LoadWhile(LitElement) {
32+
@property({ type: Boolean }) accessor isActive: boolean;
33+
@property({ type: Object }) accessor apiService: ApiService | null;
34+
35+
@state() private accessor filterController: FilterController<FilterKeys>;
36+
@state() private accessor template: Partial<EmailTemplate>[] = [];
37+
@state() private accessor templateId: string;
38+
39+
#templatesAreDirty = true;
40+
41+
//Date range props
42+
@state() private accessor startDate: string;
43+
@state() private accessor endDate: string;
44+
45+
@query('md-dialog') private accessor dialog!: MdDialog;
46+
@query('titanium-date-range-selector') private accessor dateRangeSelect!: TitaniumDateRangeSelector;
47+
48+
async firstUpdated() {
49+
this.filterController.subscribeToFilterChange(async () => {
50+
this.#preloadChipData();
51+
this.requestUpdate('filterController');
52+
});
53+
}
54+
55+
async updated(changedProps: PropertyValues<this>) {
56+
if (this.isActive && changedProps.has('isActive')) {
57+
this.#preloadChipData();
58+
}
59+
}
60+
61+
async #preloadChipData() {
62+
//Preload for chips
63+
if (this.filterController.getValue('template') && this.#templatesAreDirty) {
64+
this.template = await this.#getTemplatesAsync();
65+
}
66+
}
67+
68+
async #getTemplatesAsync() {
69+
if (!this.apiService) {
70+
console.warn('No api service provided');
71+
return [];
72+
}
73+
74+
const odataParts = ['select=Id,Name,IsExpired', 'orderby=Name'];
75+
76+
try {
77+
const get = this.apiService.getAsync<EmailTemplate>(`EmailTemplates?${odataParts.join('&')}`);
78+
this.loadWhile(get);
79+
const entities = (await get).toList();
80+
this.#templatesAreDirty = false;
81+
return entities;
82+
} catch (error) {
83+
this.dispatchEvent(new ShowSnackbarEvent(error));
84+
}
85+
return [];
86+
}
87+
88+
public async open() {
89+
if (this.#templatesAreDirty) {
90+
this.template = await this.#getTemplatesAsync();
91+
}
92+
93+
this.templateId = this.filterController.getValue('template') ?? '';
94+
95+
//populate date range
96+
const dateRange = this.filterController.getValue('dateRange') as DateRangeKey;
97+
this.startDate = (dateRange === 'custom' ? this.filterController.getValue('startDate') : DateRanges.get(dateRange)?.startDate()) || '';
98+
this.endDate = (dateRange === 'custom' ? this.filterController.getValue('endDate') : DateRanges.get(dateRange)?.endDate()) || '';
99+
100+
this.dialog.show();
101+
}
102+
103+
static styles = [
104+
css`
105+
:host {
106+
display: flex;
107+
flex-wrap: wrap;
108+
align-items: center;
109+
gap: 8px;
110+
}
111+
112+
md-dialog {
113+
max-width: 550px;
114+
width: calc(100vw - 24px);
115+
116+
div[inactive] {
117+
font-size: 12px;
118+
line-height: 14px;
119+
opacity: 0.8;
120+
}
121+
122+
md-filled-select {
123+
width: 100%;
124+
margin-top: 24px;
125+
--md-filled-select-text-field-container-shape: 16px;
126+
--md-filled-select-text-field-active-indicator-height: 0;
127+
--md-filled-select-text-field-error-active-indicator-height: 0;
128+
--md-filled-select-text-field-hover-active-indicator-height: 0;
129+
--md-filled-select-text-field-focus-active-indicator-height: 0;
130+
--md-filled-select-text-field-disabled-active-indicator-height: 0;
131+
}
132+
}
133+
134+
md-input-chip {
135+
background: var(--md-sys-color-surface-container);
136+
--md-sys-color-outline: transparent;
137+
}
138+
139+
[hidden] {
140+
display: none !important;
141+
}
142+
`,
143+
];
144+
145+
render() {
146+
return html`
147+
<md-input-chip
148+
remove-only
149+
?hidden=${!this.filterController.getValue('template') || this.isLoading}
150+
label="${this.template.find((o) => o.Id === Number(this.filterController.getValue('template')))?.Name ?? ''} template"
151+
@remove=${(e: Event) => {
152+
e.preventDefault();
153+
this.filterController.setValue('template', null);
154+
}}
155+
>
156+
<md-icon slot="icon">content_copy</md-icon>
157+
</md-input-chip>
158+
159+
<md-input-chip
160+
remove-only
161+
?hidden=${!(this.filterController.getValue('dateRange') as DateRangeKey) || (this.filterController.getValue('dateRange') as DateRangeKey) === 'allTime'}
162+
label=${rangeLabel(
163+
this.filterController.getValue('dateRange') as DateRangeKey,
164+
this.filterController.getValue('startDate') ?? null,
165+
this.filterController.getValue('endDate') ?? null,
166+
'Sent'
167+
)}
168+
@remove=${(e: Event) => {
169+
e.preventDefault();
170+
this.filterController.setValue('dateRange', null);
171+
this.filterController.setValue('startDate', null);
172+
this.filterController.setValue('endDate', null);
173+
}}
174+
>
175+
<md-icon slot="icon">date_range</md-icon>
176+
</md-input-chip>
177+
178+
<md-dialog
179+
@open=${(e: DOMEvent<MdDialog>) => {
180+
dialogZIndexHack(e.target);
181+
dialogOpenNavigationHack(e.target);
182+
}}
183+
@close=${(e: DOMEvent<MdDialog>) => {
184+
dialogCloseNavigationHack(e.target);
185+
}}
186+
>
187+
<div slot="headline">Filter logs by</div>
188+
<form slot="content" method="dialog">
189+
<titanium-date-range-selector
190+
filled
191+
label="Sent"
192+
.startDate=${this.startDate}
193+
.endDate=${this.endDate}
194+
@change=${(event: DOMEvent<TitaniumDateRangeSelector>) => {
195+
this.startDate = event.target.startDate || '';
196+
this.endDate = event.target.endDate || '';
197+
}}
198+
></titanium-date-range-selector>
199+
200+
<md-filled-select label="Templates" .value=${this.templateId ?? ''} @change=${(e: DOMEvent<MdOutlinedSelect>) => (this.templateId = e.target.value)}>
201+
<md-icon slot="leading-icon">content_copy</md-icon>
202+
<md-select-option></md-select-option>
203+
${this.template.map(
204+
(o) =>
205+
html`<md-select-option ?selected=${o.Id === Number(this.templateId)} value=${o.Id ?? ''}>
206+
<div slot="headline">${o.Name}</div>
207+
${o.IsExpired ? html`<div inactive slot="supporting-text">Inactive</div>` : ''}
208+
<md-icon slot="start">content_copy</md-icon>
209+
</md-select-option>`
210+
)}
211+
</md-filled-select>
212+
</form>
213+
<div slot="actions">
214+
<md-text-button @click=${() => this.dialog.close('cancel')}> Close </md-text-button>
215+
<md-filled-tonal-button
216+
@click=${() => {
217+
this.filterController.setValue('template', this.templateId || null);
218+
219+
//set date range
220+
this.filterController.setValue('dateRange', this.dateRangeSelect.range === 'allTime' ? null : this.dateRangeSelect.range);
221+
this.filterController.setValue('startDate', this.dateRangeSelect.range === 'custom' ? this.startDate || null : null);
222+
this.filterController.setValue('endDate', this.dateRangeSelect.range === 'custom' ? this.endDate || null : null);
223+
224+
this.dialog.close('apply');
225+
}}
226+
>Apply</md-filled-tonal-button
227+
>
228+
</div>
229+
</md-dialog>
230+
`;
231+
}
232+
}

0 commit comments

Comments
 (0)