@ticatec/uniface-element 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +320 -1
- package/README_CN.md +392 -0
- package/dist/accordion/Accordion.svelte +1 -1
- package/dist/action-bar/ActionBar.svelte +7 -1
- package/dist/attachment-files/FileRender.svelte +1 -1
- package/dist/attachment-files/FileUploadBar.svelte +7 -6
- package/dist/attachment-files/FileUploadPanel.svelte +2 -2
- package/dist/base-calendar/DateContext.js +5 -4
- package/dist/base-calendar/YearPickupPanel.svelte +4 -4
- package/dist/card/Card.svelte +1 -1
- package/dist/card/Card.svelte.d.ts +1 -1
- package/dist/cascade-options-select/CascadeOptionSelect.svelte +2 -3
- package/dist/cascade-options-select/CascadeOptionSelect.svelte.d.ts +46 -1
- package/dist/cascade-options-select/CascadePanel.svelte +1 -1
- package/dist/checkbox/CheckBox.svelte +1 -1
- package/dist/color-picker/ColorPicker.svelte +9 -9
- package/dist/color-picker/ColorPicker.svelte.d.ts +0 -2
- package/dist/color-picker/ColorPickerPanel.svelte +11 -15
- package/dist/color-picker/ColorPickerPanel.svelte.d.ts +1 -1
- package/dist/common/CommonPicker.svelte +2 -2
- package/dist/common/CommonPicker.svelte.d.ts +1 -1
- package/dist/common/MouseClickHandler.d.ts +1 -1
- package/dist/common/Popover.svelte +28 -6
- package/dist/common/Popover.svelte.d.ts +9 -3
- package/dist/common/uniface-utils.js +6 -4
- package/dist/common-editor/CommonEditor.svelte +1 -1
- package/dist/common-editor/CommonEditor.svelte.d.ts +1 -1
- package/dist/common-editor/NumberInput.svelte +1 -1
- package/dist/common-editor/NumberInput.svelte.d.ts +1 -1
- package/dist/concise-data-table/ConciseListTable.svelte +7 -2
- package/dist/concise-data-table/ConciseListTable.svelte.d.ts +1 -0
- package/dist/concise-data-table/DataRow.svelte +1 -1
- package/dist/concise-data-table/TableOptions.d.ts +1 -1
- package/dist/data-table/DataTable.svelte +1 -1
- package/dist/data-table/parts/ActionsPanel.svelte +4 -3
- package/dist/data-table/parts/ActionsRow.svelte +3 -3
- package/dist/data-table/parts/ContentPanel.svelte +3 -2
- package/dist/data-table/parts/FixedColumnsPanel.svelte +0 -1
- package/dist/data-table/parts/FixedColumnsPanel.svelte.d.ts +1 -2
- package/dist/data-table/parts/FixedHeaderPanel.svelte +2 -2
- package/dist/data-table/parts/FixedRow.svelte +2 -1
- package/dist/data-table/parts/FixedRow.svelte.d.ts +1 -1
- package/dist/data-table/parts/HrefCell.svelte +23 -2
- package/dist/data-table/parts/PopupMenu.svelte +25 -2
- package/dist/data-table/parts/PopupMenu.svelte.d.ts +8 -1
- package/dist/date-picker/DatePicker.svelte +0 -1
- package/dist/date-picker/DateTimePicker.svelte +3 -3
- package/dist/date-picker/ScrollBar.svelte +4 -7
- package/dist/date-range/DateRangeEditor.svelte +1 -1
- package/dist/dialog/CommonDialog.svelte +8 -5
- package/dist/dialog/CommonDialog.svelte.d.ts +2 -1
- package/dist/dialog/Dialog.svelte +37 -12
- package/dist/dialog/Dialog.svelte.d.ts +4 -3
- package/dist/dialog/DialogBoard.svelte +11 -8
- package/dist/dialog/DialogWrapper.svelte +10 -0
- package/dist/{common-editor/DateInput.svelte.d.ts → dialog/DialogWrapper.svelte.d.ts} +6 -13
- package/dist/dialog/Dialogs.js +1 -1
- package/dist/dialog/ModalResult.d.ts +5 -0
- package/dist/dialog/ModalResult.js +5 -0
- package/dist/drawer/Drawer.svelte +1 -1
- package/dist/form-field/FormField.svelte +2 -1
- package/dist/form-field/FormField.svelte.d.ts +1 -0
- package/dist/form-panel/flex-row-form/FormContainer.svelte +1 -1
- package/dist/form-panel/grid-form/EmptyCell.svelte +8 -7
- package/dist/form-panel/grid-form/EmptyCell.svelte.d.ts +2 -2
- package/dist/form-panel/grid-form/GridField.svelte +1 -1
- package/dist/i18nRes/i18nRes.d.ts +2 -0
- package/dist/i18nRes/i18nRes.js +44 -0
- package/dist/i18nRes/index.d.ts +2 -0
- package/dist/i18nRes/index.js +2 -0
- package/dist/image-files/ImageFilesField.svelte +1 -1
- package/dist/indicator/IndicatorBoard.svelte +1 -1
- package/dist/inline-cell-editor/InvalidEditor.svelte +1 -0
- package/dist/input-options-select/InputOptionsSelect.svelte +6 -6
- package/dist/input-options-select/InputOptionsSelect.svelte.d.ts +1 -1
- package/dist/layout/flex/FlexBlock.svelte +1 -1
- package/dist/lookup-editor/LookupEditor.svelte +1 -1
- package/dist/memo-editor/MemoEditor.svelte +2 -2
- package/dist/message-box/IMessageBox.d.ts +2 -4
- package/dist/message-box/IMessageBox.js +4 -6
- package/dist/message-box/MessageBoxBoard.svelte +1 -3
- package/dist/options-multi-select/OptionsMultiSelect.svelte +1 -1
- package/dist/prompts-text-editor/PromptsTextEditor.svelte +2 -2
- package/dist/prompts-text-editor/PromptsTextEditor.svelte.d.ts +1 -1
- package/dist/property-editor/PropertyEditor.svelte +3 -3
- package/dist/text-editor/PasswordEditor.svelte +1 -1
- package/dist/transfer/Transfer.svelte +17 -12
- package/dist/transfer/Transfer.svelte.d.ts +0 -1
- package/dist/unit-number-editor/UnitNumberEditor.svelte +2 -2
- package/docs/buttons/cn/ActionBar.md +459 -0
- package/docs/buttons/cn/Button.md +228 -0
- package/docs/buttons/cn/IconButton.md +294 -0
- package/docs/buttons/cn/README.md +600 -0
- package/docs/buttons/cn/TextButton.md +163 -0
- package/docs/buttons/en/ActionBar.md +459 -0
- package/docs/buttons/en/Button.md +228 -0
- package/docs/buttons/en/IconButton.md +294 -0
- package/docs/buttons/en/README.md +600 -0
- package/docs/buttons/en/TextButton.md +163 -0
- package/docs/containers/cn/accordion.md +622 -0
- package/docs/containers/cn/box.md +445 -0
- package/docs/containers/cn/card.md +404 -0
- package/docs/containers/cn/page.md +576 -0
- package/docs/containers/cn/tabs.md +686 -0
- package/docs/containers/en/accordion.md +622 -0
- package/docs/containers/en/box.md +445 -0
- package/docs/containers/en/card.md +404 -0
- package/docs/containers/en/page.md +576 -0
- package/docs/containers/en/tabs.md +686 -0
- package/docs/data-display/cn/concise-data-table.md +731 -0
- package/docs/data-display/cn/listbox.md +786 -0
- package/docs/data-display/cn/treeview.md +863 -0
- package/docs/data-display/en/concise-data-table.md +732 -0
- package/docs/data-display/en/listbox.md +786 -0
- package/docs/data-display/en/treeview.md +863 -0
- package/docs/data-table/cn/README.md +596 -0
- package/docs/data-table/en/README.md +596 -0
- package/docs/dialog/cn/README.md +767 -0
- package/docs/dialog/en/README.md +767 -0
- package/docs/form/cn/README.md +847 -0
- package/docs/form/en/README.md +847 -0
- package/docs/form-components/cn/README.md +495 -0
- package/docs/form-components/cn/attachmentfilesfield.md +757 -0
- package/docs/form-components/cn/cascade-options-select.md +374 -0
- package/docs/form-components/cn/checkbox.md +58 -0
- package/docs/form-components/cn/date-picker.md +138 -0
- package/docs/form-components/cn/datetime-picker.md +228 -0
- package/docs/form-components/cn/group-checkbox.md +405 -0
- package/docs/form-components/cn/group-radiobox.md +525 -0
- package/docs/form-components/cn/imagefilesfield.md +651 -0
- package/docs/form-components/cn/input-options-select.md +250 -0
- package/docs/form-components/cn/lookup-editor.md +406 -0
- package/docs/form-components/cn/memoeditor.md +409 -0
- package/docs/form-components/cn/number-editor.md +261 -0
- package/docs/form-components/cn/options-multi-select.md +177 -0
- package/docs/form-components/cn/options-select.md +83 -0
- package/docs/form-components/cn/passwordeditor.md +538 -0
- package/docs/form-components/cn/prompts-text-editor.md +241 -0
- package/docs/form-components/cn/radiobutton.md +84 -0
- package/docs/form-components/cn/switch.md +178 -0
- package/docs/form-components/cn/text-editor.md +142 -0
- package/docs/form-components/cn/time-editor.md +159 -0
- package/docs/form-components/cn/unit-number-editor.md +164 -0
- package/docs/form-components/en/README.md +463 -0
- package/docs/form-components/en/attachmentfilesfield.md +757 -0
- package/docs/form-components/en/cascade-options-select.md +358 -0
- package/docs/form-components/en/checkbox.md +59 -0
- package/docs/form-components/en/date-picker.md +138 -0
- package/docs/form-components/en/datetime-picker.md +228 -0
- package/docs/form-components/en/group-checkbox.md +395 -0
- package/docs/form-components/en/group-radiobox.md +498 -0
- package/docs/form-components/en/imagefilesfield.md +651 -0
- package/docs/form-components/en/input-options-select.md +239 -0
- package/docs/form-components/en/lookup-editor.md +389 -0
- package/docs/form-components/en/memoeditor.md +409 -0
- package/docs/form-components/en/number-editor.md +249 -0
- package/docs/form-components/en/options-multi-select.md +177 -0
- package/docs/form-components/en/options-select.md +83 -0
- package/docs/form-components/en/passwordeditor.md +538 -0
- package/docs/form-components/en/prompts-text-editor.md +241 -0
- package/docs/form-components/en/radiobutton.md +84 -0
- package/docs/form-components/en/switch.md +180 -0
- package/docs/form-components/en/text-editor.md +142 -0
- package/docs/form-components/en/time-editor.md +159 -0
- package/docs/form-components/en/unit-number-editor.md +164 -0
- package/docs/form-layout/cn/criteria-field.md +652 -0
- package/docs/form-layout/cn/flex-form.md +454 -0
- package/docs/form-layout/cn/flex-row-form.md +542 -0
- package/docs/form-layout/cn/form-field.md +923 -0
- package/docs/form-layout/cn/grid-form.md +676 -0
- package/docs/form-layout/en/criteria-field.md +652 -0
- package/docs/form-layout/en/flex-form.md +454 -0
- package/docs/form-layout/en/flex-row-form.md +542 -0
- package/docs/form-layout/en/form-field.md +923 -0
- package/docs/form-layout/en/grid-form.md +676 -0
- package/docs/global/cn/README.md +534 -0
- package/docs/global/en/README.md +534 -0
- package/docs/layout/cn/classiclayout.md +460 -0
- package/docs/layout/cn/headerlayout.md +524 -0
- package/docs/layout/cn/sidebarlayout.md +263 -0
- package/docs/layout/en/classiclayout.md +951 -0
- package/docs/layout/en/headerlayout.md +524 -0
- package/docs/layout/en/sidebarlayout.md +262 -0
- package/docs/misc/cn/README.md +1217 -0
- package/docs/misc/en/README.md +1217 -0
- package/docs/navigation/cn/breadcrumb.md +291 -0
- package/docs/navigation/cn/navigator.md +322 -0
- package/docs/navigation/cn/navigatormenu.md +512 -0
- package/docs/navigation/cn/progressbar.md +199 -0
- package/docs/navigation/cn/progressstepbar.md +299 -0
- package/docs/navigation/en/breadcrumb.md +291 -0
- package/docs/navigation/en/navigator.md +322 -0
- package/docs/navigation/en/navigatormenu.md +512 -0
- package/docs/navigation/en/progressbar.md +199 -0
- package/docs/navigation/en/progressstepbar.md +299 -0
- package/docs/search-filters/cn/daterange.md +1156 -0
- package/docs/search-filters/cn/numberrange.md +688 -0
- package/docs/search-filters/cn/searchbox.md +1582 -0
- package/docs/search-filters/en/daterange.md +1156 -0
- package/docs/search-filters/en/numberrange.md +688 -0
- package/docs/search-filters/en/searchbox.md +1582 -0
- package/package.json +53 -13
- package/dist/common-editor/DateInput.svelte +0 -27
- package/dist/i18n_resources/index.d.ts +0 -3
- package/dist/i18n_resources/index.js +0 -3
- /package/dist/{i18n_resources → i18nRes}/uniface_cn_resource.d.ts +0 -0
- /package/dist/{i18n_resources → i18nRes}/uniface_cn_resource.js +0 -0
- /package/dist/{i18n_resources → i18nRes}/uniface_en_resource.d.ts +0 -0
- /package/dist/{i18n_resources → i18nRes}/uniface_en_resource.js +0 -0
|
@@ -0,0 +1,1156 @@
|
|
|
1
|
+
# DateRange Component
|
|
2
|
+
|
|
3
|
+
A date range selector component that allows users to select a start and end date through an intuitive dual calendar interface, commonly used for filtering data by date periods such as reporting ranges, booking periods, or event schedules.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The DateRange component provides a comprehensive date selection experience with dual calendar displays, flexible date formatting, and robust validation. It features readonly input fields that open a popover with side-by-side calendars for easy range selection. The component supports various date formats, minimum/maximum date constraints, and seamless integration with form validation systems.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
import DateRange from '@ticatec/uniface-element/DateRange';
|
|
13
|
+
import type { UniDate } from '@ticatec/uniface-element/DateRange';
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Basic Usage
|
|
17
|
+
|
|
18
|
+
```svelte
|
|
19
|
+
<script lang="ts">
|
|
20
|
+
import DateRange from '@ticatec/uniface-element/DateRange';
|
|
21
|
+
import type { UniDate } from '@ticatec/uniface-element/DateRange';
|
|
22
|
+
|
|
23
|
+
let fromDate: UniDate = null;
|
|
24
|
+
let toDate: UniDate = null;
|
|
25
|
+
</script>
|
|
26
|
+
|
|
27
|
+
<DateRange
|
|
28
|
+
bind:fromValue={fromDate}
|
|
29
|
+
bind:toValue={toDate}
|
|
30
|
+
variant="outlined"
|
|
31
|
+
/>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Properties
|
|
35
|
+
|
|
36
|
+
| Property | Type | Default | Description |
|
|
37
|
+
|----------|------|---------|-------------|
|
|
38
|
+
| `variant` | `'' \| 'plain' \| 'outlined' \| 'filled'` | `''` | Visual styling variant |
|
|
39
|
+
| `compact` | `boolean` | `false` | Enable compact layout for dense forms |
|
|
40
|
+
| `class` | `string` | `''` | Additional CSS class names |
|
|
41
|
+
| `style` | `string` | `''` | Custom inline styles |
|
|
42
|
+
| `fromValue` | `UniDate` | `null` | Start date (bindable) |
|
|
43
|
+
| `toValue` | `UniDate` | `null` | End date (bindable) |
|
|
44
|
+
| `format` | `string` | `"YYYY-MM-DD"` | Date display format (dayjs format) |
|
|
45
|
+
| `min` | `string \| Date \| null` | `null` | Minimum selectable date |
|
|
46
|
+
| `max` | `string \| Date \| null` | `null` | Maximum selectable date |
|
|
47
|
+
|
|
48
|
+
## Type Definitions
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
/**
|
|
52
|
+
* Flexible date type supporting multiple formats
|
|
53
|
+
*/
|
|
54
|
+
type UniDate = dayjs.Dayjs | Date | string | null;
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Features
|
|
58
|
+
|
|
59
|
+
- **Dual Calendar Interface**: Side-by-side calendar view for intuitive range selection
|
|
60
|
+
- **Flexible Date Types**: Supports Date objects, strings, and dayjs instances
|
|
61
|
+
- **Custom Formatting**: Configurable date display format using dayjs patterns
|
|
62
|
+
- **Range Validation**: Automatic validation ensuring from date ≤ to date
|
|
63
|
+
- **Min/Max Constraints**: Optional date boundaries to restrict selection
|
|
64
|
+
- **Popover Calendar**: Clean calendar popup with integrated date picker
|
|
65
|
+
- **Clear Functionality**: Built-in clear action when dates are selected
|
|
66
|
+
- **Readonly Inputs**: Prevents manual typing for consistent date format
|
|
67
|
+
|
|
68
|
+
## Usage Examples
|
|
69
|
+
|
|
70
|
+
### Reporting Date Range Selector
|
|
71
|
+
|
|
72
|
+
```svelte
|
|
73
|
+
<script lang="ts">
|
|
74
|
+
import DateRange from '@ticatec/uniface-element/DateRange';
|
|
75
|
+
import type { UniDate } from '@ticatec/uniface-element/DateRange';
|
|
76
|
+
import dayjs from 'dayjs';
|
|
77
|
+
|
|
78
|
+
let reportFrom: UniDate = dayjs().subtract(30, 'day');
|
|
79
|
+
let reportTo: UniDate = dayjs();
|
|
80
|
+
|
|
81
|
+
// Sales data for demo
|
|
82
|
+
let salesData = [
|
|
83
|
+
{ date: '2024-03-01', sales: 12500, orders: 45 },
|
|
84
|
+
{ date: '2024-03-02', sales: 8900, orders: 32 },
|
|
85
|
+
{ date: '2024-03-03', sales: 15600, orders: 58 },
|
|
86
|
+
{ date: '2024-03-04', sales: 11200, orders: 41 },
|
|
87
|
+
{ date: '2024-03-05', sales: 9800, orders: 35 }
|
|
88
|
+
];
|
|
89
|
+
|
|
90
|
+
let filteredData = [];
|
|
91
|
+
let totalSales = 0;
|
|
92
|
+
let totalOrders = 0;
|
|
93
|
+
|
|
94
|
+
// Filter data based on selected date range
|
|
95
|
+
function filterSalesData() {
|
|
96
|
+
if (!reportFrom || !reportTo) {
|
|
97
|
+
filteredData = salesData;
|
|
98
|
+
} else {
|
|
99
|
+
const fromDate = dayjs(reportFrom);
|
|
100
|
+
const toDate = dayjs(reportTo);
|
|
101
|
+
|
|
102
|
+
filteredData = salesData.filter(item => {
|
|
103
|
+
const itemDate = dayjs(item.date);
|
|
104
|
+
return itemDate.isBetween(fromDate, toDate, 'day', '[]');
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Calculate totals
|
|
109
|
+
totalSales = filteredData.reduce((sum, item) => sum + item.sales, 0);
|
|
110
|
+
totalOrders = filteredData.reduce((sum, item) => sum + item.orders, 0);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Reactive filtering
|
|
114
|
+
$: {
|
|
115
|
+
reportFrom, reportTo;
|
|
116
|
+
filterSalesData();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Quick date range presets
|
|
120
|
+
function setDateRange(days: number) {
|
|
121
|
+
reportFrom = dayjs().subtract(days, 'day');
|
|
122
|
+
reportTo = dayjs();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function setThisMonth() {
|
|
126
|
+
reportFrom = dayjs().startOf('month');
|
|
127
|
+
reportTo = dayjs().endOf('month');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function setLastMonth() {
|
|
131
|
+
reportFrom = dayjs().subtract(1, 'month').startOf('month');
|
|
132
|
+
reportTo = dayjs().subtract(1, 'month').endOf('month');
|
|
133
|
+
}
|
|
134
|
+
</script>
|
|
135
|
+
|
|
136
|
+
<div class="sales-report">
|
|
137
|
+
<header class="report-header">
|
|
138
|
+
<h2>Sales Report Dashboard</h2>
|
|
139
|
+
<p>Select a date range to view sales analytics</p>
|
|
140
|
+
</header>
|
|
141
|
+
|
|
142
|
+
<div class="date-controls">
|
|
143
|
+
<div class="date-range-section">
|
|
144
|
+
<label>Report Period:</label>
|
|
145
|
+
<DateRange
|
|
146
|
+
bind:fromValue={reportFrom}
|
|
147
|
+
bind:toValue={reportTo}
|
|
148
|
+
variant="outlined"
|
|
149
|
+
format="MMM DD, YYYY"
|
|
150
|
+
style="width: 320px;"
|
|
151
|
+
/>
|
|
152
|
+
</div>
|
|
153
|
+
|
|
154
|
+
<div class="quick-presets">
|
|
155
|
+
<label>Quick Presets:</label>
|
|
156
|
+
<div class="preset-buttons">
|
|
157
|
+
<button class="preset-btn" on:click={() => setDateRange(7)}>Last 7 days</button>
|
|
158
|
+
<button class="preset-btn" on:click={() => setDateRange(30)}>Last 30 days</button>
|
|
159
|
+
<button class="preset-btn" on:click={setThisMonth}>This Month</button>
|
|
160
|
+
<button class="preset-btn" on:click={setLastMonth}>Last Month</button>
|
|
161
|
+
</div>
|
|
162
|
+
</div>
|
|
163
|
+
</div>
|
|
164
|
+
|
|
165
|
+
<div class="report-summary">
|
|
166
|
+
<div class="summary-card">
|
|
167
|
+
<h3>Total Sales</h3>
|
|
168
|
+
<div class="metric-value">${totalSales.toLocaleString()}</div>
|
|
169
|
+
</div>
|
|
170
|
+
<div class="summary-card">
|
|
171
|
+
<h3>Total Orders</h3>
|
|
172
|
+
<div class="metric-value">{totalOrders}</div>
|
|
173
|
+
</div>
|
|
174
|
+
<div class="summary-card">
|
|
175
|
+
<h3>Average Order</h3>
|
|
176
|
+
<div class="metric-value">
|
|
177
|
+
${totalOrders > 0 ? (totalSales / totalOrders).toFixed(2) : '0.00'}
|
|
178
|
+
</div>
|
|
179
|
+
</div>
|
|
180
|
+
</div>
|
|
181
|
+
|
|
182
|
+
<div class="data-table">
|
|
183
|
+
<h3>Daily Breakdown ({filteredData.length} days)</h3>
|
|
184
|
+
{#if filteredData.length > 0}
|
|
185
|
+
<table>
|
|
186
|
+
<thead>
|
|
187
|
+
<tr>
|
|
188
|
+
<th>Date</th>
|
|
189
|
+
<th>Sales</th>
|
|
190
|
+
<th>Orders</th>
|
|
191
|
+
<th>Avg Order</th>
|
|
192
|
+
</tr>
|
|
193
|
+
</thead>
|
|
194
|
+
<tbody>
|
|
195
|
+
{#each filteredData as item}
|
|
196
|
+
<tr>
|
|
197
|
+
<td>{dayjs(item.date).format('MMM DD, YYYY')}</td>
|
|
198
|
+
<td>${item.sales.toLocaleString()}</td>
|
|
199
|
+
<td>{item.orders}</td>
|
|
200
|
+
<td>${(item.sales / item.orders).toFixed(2)}</td>
|
|
201
|
+
</tr>
|
|
202
|
+
{/each}
|
|
203
|
+
</tbody>
|
|
204
|
+
</table>
|
|
205
|
+
{:else}
|
|
206
|
+
<div class="no-data">
|
|
207
|
+
<p>No sales data available for the selected date range.</p>
|
|
208
|
+
</div>
|
|
209
|
+
{/if}
|
|
210
|
+
</div>
|
|
211
|
+
</div>
|
|
212
|
+
|
|
213
|
+
<style>
|
|
214
|
+
.sales-report {
|
|
215
|
+
max-width: 900px;
|
|
216
|
+
margin: 0 auto;
|
|
217
|
+
padding: 20px;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.report-header {
|
|
221
|
+
text-align: center;
|
|
222
|
+
margin-bottom: 30px;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.report-header h2 {
|
|
226
|
+
color: #333;
|
|
227
|
+
margin: 0 0 8px 0;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.report-header p {
|
|
231
|
+
color: #666;
|
|
232
|
+
margin: 0;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.date-controls {
|
|
236
|
+
display: flex;
|
|
237
|
+
flex-wrap: wrap;
|
|
238
|
+
gap: 30px;
|
|
239
|
+
align-items: flex-end;
|
|
240
|
+
margin-bottom: 30px;
|
|
241
|
+
padding: 20px;
|
|
242
|
+
background: #f8f9fa;
|
|
243
|
+
border-radius: 8px;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
.date-range-section,
|
|
247
|
+
.quick-presets {
|
|
248
|
+
flex: 1;
|
|
249
|
+
min-width: 280px;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.date-range-section label,
|
|
253
|
+
.quick-presets label {
|
|
254
|
+
display: block;
|
|
255
|
+
font-weight: 500;
|
|
256
|
+
margin-bottom: 8px;
|
|
257
|
+
color: #333;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.preset-buttons {
|
|
261
|
+
display: flex;
|
|
262
|
+
flex-wrap: wrap;
|
|
263
|
+
gap: 8px;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.preset-btn {
|
|
267
|
+
padding: 6px 12px;
|
|
268
|
+
background: #e9ecef;
|
|
269
|
+
border: 1px solid #ced4da;
|
|
270
|
+
border-radius: 4px;
|
|
271
|
+
cursor: pointer;
|
|
272
|
+
font-size: 0.875rem;
|
|
273
|
+
transition: all 0.2s;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.preset-btn:hover {
|
|
277
|
+
background: #007bff;
|
|
278
|
+
color: white;
|
|
279
|
+
border-color: #007bff;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
.report-summary {
|
|
283
|
+
display: grid;
|
|
284
|
+
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
285
|
+
gap: 20px;
|
|
286
|
+
margin-bottom: 30px;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
.summary-card {
|
|
290
|
+
background: white;
|
|
291
|
+
padding: 20px;
|
|
292
|
+
border-radius: 8px;
|
|
293
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
294
|
+
text-align: center;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
.summary-card h3 {
|
|
298
|
+
margin: 0 0 12px 0;
|
|
299
|
+
color: #666;
|
|
300
|
+
font-size: 0.9rem;
|
|
301
|
+
text-transform: uppercase;
|
|
302
|
+
letter-spacing: 0.5px;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
.metric-value {
|
|
306
|
+
font-size: 2rem;
|
|
307
|
+
font-weight: 700;
|
|
308
|
+
color: #007bff;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
.data-table {
|
|
312
|
+
background: white;
|
|
313
|
+
border-radius: 8px;
|
|
314
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
315
|
+
overflow: hidden;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
.data-table h3 {
|
|
319
|
+
margin: 0;
|
|
320
|
+
padding: 20px;
|
|
321
|
+
background: #f8f9fa;
|
|
322
|
+
border-bottom: 1px solid #dee2e6;
|
|
323
|
+
color: #333;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
table {
|
|
327
|
+
width: 100%;
|
|
328
|
+
border-collapse: collapse;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
th, td {
|
|
332
|
+
text-align: left;
|
|
333
|
+
padding: 12px 20px;
|
|
334
|
+
border-bottom: 1px solid #dee2e6;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
th {
|
|
338
|
+
font-weight: 600;
|
|
339
|
+
color: #333;
|
|
340
|
+
background: #f8f9fa;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
.no-data {
|
|
344
|
+
padding: 60px 20px;
|
|
345
|
+
text-align: center;
|
|
346
|
+
color: #666;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
@media (max-width: 768px) {
|
|
350
|
+
.date-controls {
|
|
351
|
+
flex-direction: column;
|
|
352
|
+
align-items: stretch;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
.preset-buttons {
|
|
356
|
+
justify-content: center;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
.report-summary {
|
|
360
|
+
grid-template-columns: 1fr;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
table {
|
|
364
|
+
font-size: 0.875rem;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
</style>
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
### Event Booking Date Range
|
|
371
|
+
|
|
372
|
+
```svelte
|
|
373
|
+
<script lang="ts">
|
|
374
|
+
import DateRange from '@ticatec/uniface-element/DateRange';
|
|
375
|
+
import type { UniDate } from '@ticatec/uniface-element/DateRange';
|
|
376
|
+
import dayjs from 'dayjs';
|
|
377
|
+
|
|
378
|
+
let checkInDate: UniDate = null;
|
|
379
|
+
let checkOutDate: UniDate = null;
|
|
380
|
+
|
|
381
|
+
// Hotel booking data
|
|
382
|
+
let availableRooms = [
|
|
383
|
+
{ id: 1, type: 'Standard Room', price: 120, available: true },
|
|
384
|
+
{ id: 2, type: 'Deluxe Room', price: 180, available: true },
|
|
385
|
+
{ id: 3, type: 'Suite', price: 320, available: false },
|
|
386
|
+
{ id: 4, type: 'Executive Room', price: 220, available: true }
|
|
387
|
+
];
|
|
388
|
+
|
|
389
|
+
let selectedRoom = null;
|
|
390
|
+
let totalNights = 0;
|
|
391
|
+
let totalCost = 0;
|
|
392
|
+
|
|
393
|
+
// Calculate stay duration and cost
|
|
394
|
+
function calculateStay() {
|
|
395
|
+
if (checkInDate && checkOutDate) {
|
|
396
|
+
const checkin = dayjs(checkInDate);
|
|
397
|
+
const checkout = dayjs(checkOutDate);
|
|
398
|
+
totalNights = checkout.diff(checkin, 'day');
|
|
399
|
+
|
|
400
|
+
if (selectedRoom) {
|
|
401
|
+
totalCost = totalNights * selectedRoom.price;
|
|
402
|
+
}
|
|
403
|
+
} else {
|
|
404
|
+
totalNights = 0;
|
|
405
|
+
totalCost = 0;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// Reactive calculations
|
|
410
|
+
$: {
|
|
411
|
+
checkInDate, checkOutDate, selectedRoom;
|
|
412
|
+
calculateStay();
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
function selectRoom(room) {
|
|
416
|
+
selectedRoom = room;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function bookRoom() {
|
|
420
|
+
if (selectedRoom && checkInDate && checkOutDate) {
|
|
421
|
+
alert(`Booking confirmed!\nRoom: ${selectedRoom.type}\nCheck-in: ${dayjs(checkInDate).format('MMM DD, YYYY')}\nCheck-out: ${dayjs(checkOutDate).format('MMM DD, YYYY')}\nTotal: $${totalCost}`);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
</script>
|
|
425
|
+
|
|
426
|
+
<div class="booking-system">
|
|
427
|
+
<header class="booking-header">
|
|
428
|
+
<h2>Hotel Room Booking</h2>
|
|
429
|
+
<p>Select your stay dates and choose a room</p>
|
|
430
|
+
</header>
|
|
431
|
+
|
|
432
|
+
<div class="booking-form">
|
|
433
|
+
<div class="date-selection">
|
|
434
|
+
<h3>Select Your Stay Dates</h3>
|
|
435
|
+
<DateRange
|
|
436
|
+
bind:fromValue={checkInDate}
|
|
437
|
+
bind:toValue={checkOutDate}
|
|
438
|
+
variant="outlined"
|
|
439
|
+
format="MMM DD, YYYY"
|
|
440
|
+
min={dayjs().format('YYYY-MM-DD')}
|
|
441
|
+
max={dayjs().add(1, 'year').format('YYYY-MM-DD')}
|
|
442
|
+
style="width: 350px;"
|
|
443
|
+
/>
|
|
444
|
+
|
|
445
|
+
{#if totalNights > 0}
|
|
446
|
+
<div class="stay-info">
|
|
447
|
+
<p><strong>{totalNights}</strong> night{totalNights === 1 ? '' : 's'} stay</p>
|
|
448
|
+
<p>Check-in: {dayjs(checkInDate).format('dddd, MMM DD, YYYY')}</p>
|
|
449
|
+
<p>Check-out: {dayjs(checkOutDate).format('dddd, MMM DD, YYYY')}</p>
|
|
450
|
+
</div>
|
|
451
|
+
{/if}
|
|
452
|
+
</div>
|
|
453
|
+
|
|
454
|
+
{#if checkInDate && checkOutDate && totalNights > 0}
|
|
455
|
+
<div class="room-selection">
|
|
456
|
+
<h3>Available Rooms</h3>
|
|
457
|
+
<div class="rooms-grid">
|
|
458
|
+
{#each availableRooms as room}
|
|
459
|
+
<div
|
|
460
|
+
class="room-card"
|
|
461
|
+
class:selected={selectedRoom?.id === room.id}
|
|
462
|
+
class:unavailable={!room.available}
|
|
463
|
+
on:click={() => room.available && selectRoom(room)}
|
|
464
|
+
>
|
|
465
|
+
<div class="room-type">{room.type}</div>
|
|
466
|
+
<div class="room-price">${room.price}/night</div>
|
|
467
|
+
<div class="room-total">
|
|
468
|
+
{#if room.available}
|
|
469
|
+
Total: ${room.price * totalNights} for {totalNights} nights
|
|
470
|
+
{:else}
|
|
471
|
+
<span class="unavailable-text">Not Available</span>
|
|
472
|
+
{/if}
|
|
473
|
+
</div>
|
|
474
|
+
{#if selectedRoom?.id === room.id}
|
|
475
|
+
<div class="selected-indicator">✓ Selected</div>
|
|
476
|
+
{/if}
|
|
477
|
+
</div>
|
|
478
|
+
{/each}
|
|
479
|
+
</div>
|
|
480
|
+
</div>
|
|
481
|
+
|
|
482
|
+
{#if selectedRoom}
|
|
483
|
+
<div class="booking-summary">
|
|
484
|
+
<h3>Booking Summary</h3>
|
|
485
|
+
<div class="summary-details">
|
|
486
|
+
<div class="detail-row">
|
|
487
|
+
<span>Room Type:</span>
|
|
488
|
+
<span>{selectedRoom.type}</span>
|
|
489
|
+
</div>
|
|
490
|
+
<div class="detail-row">
|
|
491
|
+
<span>Check-in Date:</span>
|
|
492
|
+
<span>{dayjs(checkInDate).format('MMM DD, YYYY')}</span>
|
|
493
|
+
</div>
|
|
494
|
+
<div class="detail-row">
|
|
495
|
+
<span>Check-out Date:</span>
|
|
496
|
+
<span>{dayjs(checkOutDate).format('MMM DD, YYYY')}</span>
|
|
497
|
+
</div>
|
|
498
|
+
<div class="detail-row">
|
|
499
|
+
<span>Number of Nights:</span>
|
|
500
|
+
<span>{totalNights}</span>
|
|
501
|
+
</div>
|
|
502
|
+
<div class="detail-row">
|
|
503
|
+
<span>Rate per Night:</span>
|
|
504
|
+
<span>${selectedRoom.price}</span>
|
|
505
|
+
</div>
|
|
506
|
+
<div class="detail-row total">
|
|
507
|
+
<span>Total Cost:</span>
|
|
508
|
+
<span>${totalCost}</span>
|
|
509
|
+
</div>
|
|
510
|
+
</div>
|
|
511
|
+
|
|
512
|
+
<button class="book-btn" on:click={bookRoom}>
|
|
513
|
+
Book Room - ${totalCost}
|
|
514
|
+
</button>
|
|
515
|
+
</div>
|
|
516
|
+
{/if}
|
|
517
|
+
{/if}
|
|
518
|
+
</div>
|
|
519
|
+
</div>
|
|
520
|
+
|
|
521
|
+
<style>
|
|
522
|
+
.booking-system {
|
|
523
|
+
max-width: 800px;
|
|
524
|
+
margin: 0 auto;
|
|
525
|
+
padding: 20px;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
.booking-header {
|
|
529
|
+
text-align: center;
|
|
530
|
+
margin-bottom: 30px;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
.booking-header h2 {
|
|
534
|
+
color: #333;
|
|
535
|
+
margin: 0 0 8px 0;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
.booking-header p {
|
|
539
|
+
color: #666;
|
|
540
|
+
margin: 0;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
.booking-form {
|
|
544
|
+
display: flex;
|
|
545
|
+
flex-direction: column;
|
|
546
|
+
gap: 30px;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
.date-selection {
|
|
550
|
+
background: white;
|
|
551
|
+
padding: 25px;
|
|
552
|
+
border-radius: 8px;
|
|
553
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
.date-selection h3 {
|
|
557
|
+
margin: 0 0 20px 0;
|
|
558
|
+
color: #333;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
.stay-info {
|
|
562
|
+
margin-top: 20px;
|
|
563
|
+
padding: 16px;
|
|
564
|
+
background: #e3f2fd;
|
|
565
|
+
border-radius: 6px;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
.stay-info p {
|
|
569
|
+
margin: 4px 0;
|
|
570
|
+
color: #1976d2;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
.room-selection {
|
|
574
|
+
background: white;
|
|
575
|
+
padding: 25px;
|
|
576
|
+
border-radius: 8px;
|
|
577
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
.room-selection h3 {
|
|
581
|
+
margin: 0 0 20px 0;
|
|
582
|
+
color: #333;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
.rooms-grid {
|
|
586
|
+
display: grid;
|
|
587
|
+
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
|
588
|
+
gap: 16px;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
.room-card {
|
|
592
|
+
padding: 20px;
|
|
593
|
+
border: 2px solid #e9ecef;
|
|
594
|
+
border-radius: 8px;
|
|
595
|
+
cursor: pointer;
|
|
596
|
+
transition: all 0.2s;
|
|
597
|
+
position: relative;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
.room-card:hover:not(.unavailable) {
|
|
601
|
+
border-color: #007bff;
|
|
602
|
+
transform: translateY(-2px);
|
|
603
|
+
box-shadow: 0 4px 8px rgba(0,123,255,0.15);
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
.room-card.selected {
|
|
607
|
+
border-color: #007bff;
|
|
608
|
+
background: #f0f8ff;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
.room-card.unavailable {
|
|
612
|
+
opacity: 0.5;
|
|
613
|
+
cursor: not-allowed;
|
|
614
|
+
background: #f8f9fa;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
.room-type {
|
|
618
|
+
font-size: 1.2rem;
|
|
619
|
+
font-weight: 600;
|
|
620
|
+
color: #333;
|
|
621
|
+
margin-bottom: 8px;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
.room-price {
|
|
625
|
+
font-size: 1.1rem;
|
|
626
|
+
font-weight: 500;
|
|
627
|
+
color: #007bff;
|
|
628
|
+
margin-bottom: 8px;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
.room-total {
|
|
632
|
+
font-size: 0.9rem;
|
|
633
|
+
color: #666;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
.unavailable-text {
|
|
637
|
+
color: #dc3545;
|
|
638
|
+
font-weight: 500;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
.selected-indicator {
|
|
642
|
+
position: absolute;
|
|
643
|
+
top: 10px;
|
|
644
|
+
right: 10px;
|
|
645
|
+
background: #28a745;
|
|
646
|
+
color: white;
|
|
647
|
+
padding: 4px 8px;
|
|
648
|
+
border-radius: 4px;
|
|
649
|
+
font-size: 0.75rem;
|
|
650
|
+
font-weight: 500;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
.booking-summary {
|
|
654
|
+
background: white;
|
|
655
|
+
padding: 25px;
|
|
656
|
+
border-radius: 8px;
|
|
657
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
.booking-summary h3 {
|
|
661
|
+
margin: 0 0 20px 0;
|
|
662
|
+
color: #333;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
.summary-details {
|
|
666
|
+
margin-bottom: 20px;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
.detail-row {
|
|
670
|
+
display: flex;
|
|
671
|
+
justify-content: space-between;
|
|
672
|
+
padding: 8px 0;
|
|
673
|
+
border-bottom: 1px solid #f0f0f0;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
.detail-row.total {
|
|
677
|
+
font-weight: 600;
|
|
678
|
+
font-size: 1.1rem;
|
|
679
|
+
color: #333;
|
|
680
|
+
border-bottom: 2px solid #007bff;
|
|
681
|
+
margin-top: 10px;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
.book-btn {
|
|
685
|
+
width: 100%;
|
|
686
|
+
padding: 12px 24px;
|
|
687
|
+
background: #28a745;
|
|
688
|
+
color: white;
|
|
689
|
+
border: none;
|
|
690
|
+
border-radius: 6px;
|
|
691
|
+
font-size: 1.1rem;
|
|
692
|
+
font-weight: 600;
|
|
693
|
+
cursor: pointer;
|
|
694
|
+
transition: background-color 0.2s;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
.book-btn:hover {
|
|
698
|
+
background: #218838;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
@media (max-width: 768px) {
|
|
702
|
+
.rooms-grid {
|
|
703
|
+
grid-template-columns: 1fr;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
.detail-row {
|
|
707
|
+
font-size: 0.9rem;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
</style>
|
|
711
|
+
```
|
|
712
|
+
|
|
713
|
+
### Advanced Search Filters
|
|
714
|
+
|
|
715
|
+
```svelte
|
|
716
|
+
<script lang="ts">
|
|
717
|
+
import DateRange from '@ticatec/uniface-element/DateRange';
|
|
718
|
+
import type { UniDate } from '@ticatec/uniface-element/DateRange';
|
|
719
|
+
import dayjs from 'dayjs';
|
|
720
|
+
|
|
721
|
+
// Search criteria
|
|
722
|
+
let createdFrom: UniDate = null;
|
|
723
|
+
let createdTo: UniDate = null;
|
|
724
|
+
let modifiedFrom: UniDate = null;
|
|
725
|
+
let modifiedTo: UniDate = null;
|
|
726
|
+
|
|
727
|
+
// Mock document data
|
|
728
|
+
let allDocuments = [
|
|
729
|
+
{ id: 1, title: 'Project Proposal', type: 'Document', created: '2024-02-15', modified: '2024-03-01', status: 'Draft' },
|
|
730
|
+
{ id: 2, title: 'Budget Report', type: 'Spreadsheet', created: '2024-02-20', modified: '2024-02-28', status: 'Final' },
|
|
731
|
+
{ id: 3, title: 'Meeting Notes', type: 'Document', created: '2024-03-01', modified: '2024-03-05', status: 'Draft' },
|
|
732
|
+
{ id: 4, title: 'Contract Template', type: 'Template', created: '2024-01-10', modified: '2024-02-15', status: 'Final' },
|
|
733
|
+
{ id: 5, title: 'Marketing Plan', type: 'Presentation', created: '2024-03-05', modified: '2024-03-10', status: 'Review' }
|
|
734
|
+
];
|
|
735
|
+
|
|
736
|
+
let filteredDocuments = [];
|
|
737
|
+
|
|
738
|
+
// Filter documents based on date ranges
|
|
739
|
+
function filterDocuments() {
|
|
740
|
+
filteredDocuments = allDocuments.filter(doc => {
|
|
741
|
+
let matchesCreated = true;
|
|
742
|
+
let matchesModified = true;
|
|
743
|
+
|
|
744
|
+
// Check created date range
|
|
745
|
+
if (createdFrom || createdTo) {
|
|
746
|
+
const createdDate = dayjs(doc.created);
|
|
747
|
+
if (createdFrom && createdDate.isBefore(dayjs(createdFrom))) {
|
|
748
|
+
matchesCreated = false;
|
|
749
|
+
}
|
|
750
|
+
if (createdTo && createdDate.isAfter(dayjs(createdTo))) {
|
|
751
|
+
matchesCreated = false;
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
// Check modified date range
|
|
756
|
+
if (modifiedFrom || modifiedTo) {
|
|
757
|
+
const modifiedDate = dayjs(doc.modified);
|
|
758
|
+
if (modifiedFrom && modifiedDate.isBefore(dayjs(modifiedFrom))) {
|
|
759
|
+
matchesModified = false;
|
|
760
|
+
}
|
|
761
|
+
if (modifiedTo && modifiedDate.isAfter(dayjs(modifiedTo))) {
|
|
762
|
+
matchesModified = false;
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
return matchesCreated && matchesModified;
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
// Reactive filtering
|
|
771
|
+
$: {
|
|
772
|
+
createdFrom, createdTo, modifiedFrom, modifiedTo;
|
|
773
|
+
filterDocuments();
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
function clearAllFilters() {
|
|
777
|
+
createdFrom = null;
|
|
778
|
+
createdTo = null;
|
|
779
|
+
modifiedFrom = null;
|
|
780
|
+
modifiedTo = null;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
function setRecentFilter() {
|
|
784
|
+
createdFrom = dayjs().subtract(30, 'day');
|
|
785
|
+
createdTo = dayjs();
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
function exportResults() {
|
|
789
|
+
console.log('Exported documents:', filteredDocuments);
|
|
790
|
+
alert(`Exported ${filteredDocuments.length} documents`);
|
|
791
|
+
}
|
|
792
|
+
</script>
|
|
793
|
+
|
|
794
|
+
<div class="advanced-search">
|
|
795
|
+
<header class="search-header">
|
|
796
|
+
<h2>Document Search & Filter</h2>
|
|
797
|
+
<p>Use date ranges to find documents by creation and modification dates</p>
|
|
798
|
+
</header>
|
|
799
|
+
|
|
800
|
+
<div class="filter-panel">
|
|
801
|
+
<div class="filter-section">
|
|
802
|
+
<h3>Creation Date Range</h3>
|
|
803
|
+
<DateRange
|
|
804
|
+
bind:fromValue={createdFrom}
|
|
805
|
+
bind:toValue={createdTo}
|
|
806
|
+
variant="outlined"
|
|
807
|
+
format="MMM DD, YYYY"
|
|
808
|
+
compact={true}
|
|
809
|
+
style="width: 300px;"
|
|
810
|
+
/>
|
|
811
|
+
</div>
|
|
812
|
+
|
|
813
|
+
<div class="filter-section">
|
|
814
|
+
<h3>Modification Date Range</h3>
|
|
815
|
+
<DateRange
|
|
816
|
+
bind:fromValue={modifiedFrom}
|
|
817
|
+
bind:toValue={modifiedTo}
|
|
818
|
+
variant="outlined"
|
|
819
|
+
format="MMM DD, YYYY"
|
|
820
|
+
compact={true}
|
|
821
|
+
style="width: 300px;"
|
|
822
|
+
/>
|
|
823
|
+
</div>
|
|
824
|
+
|
|
825
|
+
<div class="filter-actions">
|
|
826
|
+
<button class="action-btn secondary" on:click={setRecentFilter}>Last 30 Days</button>
|
|
827
|
+
<button class="action-btn secondary" on:click={clearAllFilters}>Clear All</button>
|
|
828
|
+
{#if filteredDocuments.length > 0}
|
|
829
|
+
<button class="action-btn primary" on:click={exportResults}>Export ({filteredDocuments.length})</button>
|
|
830
|
+
{/if}
|
|
831
|
+
</div>
|
|
832
|
+
</div>
|
|
833
|
+
|
|
834
|
+
<div class="results-section">
|
|
835
|
+
<div class="results-header">
|
|
836
|
+
<h3>Search Results</h3>
|
|
837
|
+
<span class="result-count">{filteredDocuments.length} of {allDocuments.length} documents</span>
|
|
838
|
+
</div>
|
|
839
|
+
|
|
840
|
+
<div class="documents-list">
|
|
841
|
+
{#if filteredDocuments.length > 0}
|
|
842
|
+
{#each filteredDocuments as doc}
|
|
843
|
+
<div class="document-card">
|
|
844
|
+
<div class="doc-header">
|
|
845
|
+
<div class="doc-title">{doc.title}</div>
|
|
846
|
+
<div class="doc-type">{doc.type}</div>
|
|
847
|
+
</div>
|
|
848
|
+
<div class="doc-dates">
|
|
849
|
+
<div class="date-info">
|
|
850
|
+
<span class="date-label">Created:</span>
|
|
851
|
+
<span class="date-value">{dayjs(doc.created).format('MMM DD, YYYY')}</span>
|
|
852
|
+
</div>
|
|
853
|
+
<div class="date-info">
|
|
854
|
+
<span class="date-label">Modified:</span>
|
|
855
|
+
<span class="date-value">{dayjs(doc.modified).format('MMM DD, YYYY')}</span>
|
|
856
|
+
</div>
|
|
857
|
+
</div>
|
|
858
|
+
<div class="doc-status">
|
|
859
|
+
<span class="status-badge status-{doc.status.toLowerCase()}">{doc.status}</span>
|
|
860
|
+
</div>
|
|
861
|
+
</div>
|
|
862
|
+
{/each}
|
|
863
|
+
{:else}
|
|
864
|
+
<div class="no-results">
|
|
865
|
+
<h4>No documents found</h4>
|
|
866
|
+
<p>Try adjusting your date range filters or clear all filters to see more results.</p>
|
|
867
|
+
</div>
|
|
868
|
+
{/if}
|
|
869
|
+
</div>
|
|
870
|
+
</div>
|
|
871
|
+
</div>
|
|
872
|
+
|
|
873
|
+
<style>
|
|
874
|
+
.advanced-search {
|
|
875
|
+
max-width: 1000px;
|
|
876
|
+
margin: 0 auto;
|
|
877
|
+
padding: 20px;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
.search-header {
|
|
881
|
+
text-align: center;
|
|
882
|
+
margin-bottom: 30px;
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
.search-header h2 {
|
|
886
|
+
color: #333;
|
|
887
|
+
margin: 0 0 8px 0;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
.search-header p {
|
|
891
|
+
color: #666;
|
|
892
|
+
margin: 0;
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
.filter-panel {
|
|
896
|
+
background: #f8f9fa;
|
|
897
|
+
padding: 25px;
|
|
898
|
+
border-radius: 8px;
|
|
899
|
+
margin-bottom: 30px;
|
|
900
|
+
display: flex;
|
|
901
|
+
flex-wrap: wrap;
|
|
902
|
+
gap: 25px;
|
|
903
|
+
align-items: flex-end;
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
.filter-section {
|
|
907
|
+
flex: 1;
|
|
908
|
+
min-width: 280px;
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
.filter-section h3 {
|
|
912
|
+
margin: 0 0 12px 0;
|
|
913
|
+
color: #333;
|
|
914
|
+
font-size: 1rem;
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
.filter-actions {
|
|
918
|
+
display: flex;
|
|
919
|
+
flex-wrap: wrap;
|
|
920
|
+
gap: 10px;
|
|
921
|
+
align-items: center;
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
.action-btn {
|
|
925
|
+
padding: 8px 16px;
|
|
926
|
+
border: none;
|
|
927
|
+
border-radius: 4px;
|
|
928
|
+
cursor: pointer;
|
|
929
|
+
font-size: 0.875rem;
|
|
930
|
+
font-weight: 500;
|
|
931
|
+
transition: all 0.2s;
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
.action-btn.primary {
|
|
935
|
+
background: #007bff;
|
|
936
|
+
color: white;
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
.action-btn.primary:hover {
|
|
940
|
+
background: #0056b3;
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
.action-btn.secondary {
|
|
944
|
+
background: #6c757d;
|
|
945
|
+
color: white;
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
.action-btn.secondary:hover {
|
|
949
|
+
background: #5a6268;
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
.results-section {
|
|
953
|
+
background: white;
|
|
954
|
+
border-radius: 8px;
|
|
955
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
956
|
+
overflow: hidden;
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
.results-header {
|
|
960
|
+
display: flex;
|
|
961
|
+
justify-content: space-between;
|
|
962
|
+
align-items: center;
|
|
963
|
+
padding: 20px 25px;
|
|
964
|
+
background: #f8f9fa;
|
|
965
|
+
border-bottom: 1px solid #dee2e6;
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
.results-header h3 {
|
|
969
|
+
margin: 0;
|
|
970
|
+
color: #333;
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
.result-count {
|
|
974
|
+
color: #666;
|
|
975
|
+
font-size: 0.9rem;
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
.documents-list {
|
|
979
|
+
padding: 20px;
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
.document-card {
|
|
983
|
+
border: 1px solid #e9ecef;
|
|
984
|
+
border-radius: 6px;
|
|
985
|
+
padding: 16px;
|
|
986
|
+
margin-bottom: 12px;
|
|
987
|
+
transition: all 0.2s;
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
.document-card:hover {
|
|
991
|
+
border-color: #007bff;
|
|
992
|
+
box-shadow: 0 2px 4px rgba(0,123,255,0.15);
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
.doc-header {
|
|
996
|
+
display: flex;
|
|
997
|
+
justify-content: space-between;
|
|
998
|
+
align-items: center;
|
|
999
|
+
margin-bottom: 12px;
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
.doc-title {
|
|
1003
|
+
font-weight: 600;
|
|
1004
|
+
color: #333;
|
|
1005
|
+
font-size: 1.1rem;
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
.doc-type {
|
|
1009
|
+
background: #e9ecef;
|
|
1010
|
+
color: #495057;
|
|
1011
|
+
padding: 4px 8px;
|
|
1012
|
+
border-radius: 4px;
|
|
1013
|
+
font-size: 0.75rem;
|
|
1014
|
+
font-weight: 500;
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
.doc-dates {
|
|
1018
|
+
display: flex;
|
|
1019
|
+
gap: 20px;
|
|
1020
|
+
margin-bottom: 12px;
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
.date-info {
|
|
1024
|
+
font-size: 0.875rem;
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
.date-label {
|
|
1028
|
+
color: #666;
|
|
1029
|
+
margin-right: 6px;
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
.date-value {
|
|
1033
|
+
color: #333;
|
|
1034
|
+
font-weight: 500;
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
.status-badge {
|
|
1038
|
+
padding: 4px 8px;
|
|
1039
|
+
border-radius: 12px;
|
|
1040
|
+
font-size: 0.75rem;
|
|
1041
|
+
font-weight: 500;
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
.status-draft {
|
|
1045
|
+
background: #fff3cd;
|
|
1046
|
+
color: #856404;
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
.status-final {
|
|
1050
|
+
background: #d4edda;
|
|
1051
|
+
color: #155724;
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
.status-review {
|
|
1055
|
+
background: #cce5ff;
|
|
1056
|
+
color: #004085;
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
.no-results {
|
|
1060
|
+
text-align: center;
|
|
1061
|
+
padding: 60px 20px;
|
|
1062
|
+
color: #666;
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
.no-results h4 {
|
|
1066
|
+
margin: 0 0 12px 0;
|
|
1067
|
+
color: #333;
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
@media (max-width: 768px) {
|
|
1071
|
+
.filter-panel {
|
|
1072
|
+
flex-direction: column;
|
|
1073
|
+
align-items: stretch;
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
.filter-actions {
|
|
1077
|
+
justify-content: center;
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
.doc-header {
|
|
1081
|
+
flex-direction: column;
|
|
1082
|
+
align-items: flex-start;
|
|
1083
|
+
gap: 8px;
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
.doc-dates {
|
|
1087
|
+
flex-direction: column;
|
|
1088
|
+
gap: 4px;
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
</style>
|
|
1092
|
+
```
|
|
1093
|
+
|
|
1094
|
+
## Styling
|
|
1095
|
+
|
|
1096
|
+
The DateRange component uses the following CSS classes:
|
|
1097
|
+
|
|
1098
|
+
```css
|
|
1099
|
+
.uniface-range-editor {
|
|
1100
|
+
/* Main range container */
|
|
1101
|
+
display: flex;
|
|
1102
|
+
align-items: center;
|
|
1103
|
+
gap: 8px;
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
.uniface-range-editor .calendar-icon {
|
|
1107
|
+
/* Calendar trigger icon */
|
|
1108
|
+
cursor: pointer;
|
|
1109
|
+
opacity: 0.7;
|
|
1110
|
+
transition: opacity 0.2s;
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
.uniface-range-editor .calendar-icon:hover {
|
|
1114
|
+
opacity: 1;
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
/* Calendar popover styling */
|
|
1118
|
+
.calendar-popover {
|
|
1119
|
+
/* Calendar popup container */
|
|
1120
|
+
background: white;
|
|
1121
|
+
border-radius: 8px;
|
|
1122
|
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
|
1123
|
+
padding: 16px;
|
|
1124
|
+
}
|
|
1125
|
+
```
|
|
1126
|
+
|
|
1127
|
+
## API Reference
|
|
1128
|
+
|
|
1129
|
+
### DateRange Component Props
|
|
1130
|
+
|
|
1131
|
+
```typescript
|
|
1132
|
+
interface DateRangeProps {
|
|
1133
|
+
variant?: '' | 'plain' | 'outlined' | 'filled'; // Visual styling variant
|
|
1134
|
+
compact?: boolean; // Compact layout mode
|
|
1135
|
+
class?: string; // CSS class names
|
|
1136
|
+
style?: string; // Custom inline styles
|
|
1137
|
+
fromValue: UniDate; // Start date value
|
|
1138
|
+
toValue: UniDate; // End date value
|
|
1139
|
+
format?: string; // Date display format
|
|
1140
|
+
min?: string | Date | null; // Minimum selectable date
|
|
1141
|
+
max?: string | Date | null; // Maximum selectable date
|
|
1142
|
+
}
|
|
1143
|
+
```
|
|
1144
|
+
|
|
1145
|
+
## Best Practices
|
|
1146
|
+
|
|
1147
|
+
1. **Date Validation** - Always validate that the from date is before or equal to the to date
|
|
1148
|
+
2. **Format Consistency** - Use consistent date formats throughout your application
|
|
1149
|
+
3. **Min/Max Bounds** - Set appropriate date boundaries for your use case
|
|
1150
|
+
4. **User Feedback** - Provide clear visual feedback for selected date ranges
|
|
1151
|
+
5. **Mobile Support** - Test calendar interaction on touch devices
|
|
1152
|
+
6. **Accessibility** - Ensure calendar navigation works with keyboard
|
|
1153
|
+
7. **Timezone Handling** - Be explicit about timezone handling in your application
|
|
1154
|
+
8. **Clear Functionality** - Allow users to easily clear date selections
|
|
1155
|
+
9. **Performance** - Debounce filtering operations that depend on date ranges
|
|
1156
|
+
10. **Localization** - Consider date format preferences for international users
|