Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/34/reminder types #37

Merged
merged 4 commits into from
Aug 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,32 @@ _TBD_

---

## Reminder Type Config

| Key | Value | Info |
| -------- | -------------- | ---- |
| categroy | `c8y.reminder` | |
| key | `types` | |

```json
[
{
"id": "1",
"name": "Type 1"
},
{
"id": "2",
"name": "My Preferred Type"
},
{
"id": "a_123",
"name": "A123"
}
]
```

---

## Useful links

### 📘 Explore the Knowledge Base
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,24 @@ <h2 translate>Reminder</h2>
</ul>

<div class="body">
<!-- filter: reminder type -->
<div *ngIf="types.length">
<label for="reminder-type-filter" translate>Filter by Reminder Type</label>
<div class="c8y-select-wrapper">
<select
id="reminder-type-filter"
name="reminder-type"
class="form-control"
(change)="filterReminders()"
[(ngModel)]="typeFilter"
>
<option value="">{{ 'Not filtered' | translate }}</option>
<option *ngFor="let type of types" [ngValue]="type.id">{{ type.name }}</option>
</select>
</div>
</div>

<!-- list of reminders -->
<ul class="reminder-group-list">
<li *ngFor="let group of reminderGroups; let i = index" class="reminder-group-item">
<header>
Expand All @@ -30,6 +48,13 @@ <h2 translate>Reminder</h2>
[class.text-danger]="group.status === reminderGroupStatus.due && group.count > 0"
>
{{ group.count || 0 }}
<small
*ngIf="group.total"
title="{{ 'Number of reminders if not filtered' | translate }}"
class="text-muted"
>
/ {{ group.total }}
</small>
</span>
<em>{{ group.status | translate }}</em>
<i [c8yIcon]="'expand-arrow'" [class.expanded]="groupIsExpanded[i]"></i>
Expand Down Expand Up @@ -136,6 +161,12 @@ <h2 translate>Reminder</h2>
</p>

<footer>
<!-- type -->
<button *ngIf="types.length && reminder.reminderType" type="button" class="btn btn-clean" (click)="setTypeFilter(reminder.reminderType)">
<i class="m-r-4" [c8yIcon]="'tag'"></i>
<c8y-reminder-type [id]="reminder.reminderType"></c8y-reminder-type>
</button>
<!-- asset reference -->
<ng-container *ngIf="reminder.isGroup; else isDevice">
<a [routerLink]="['/group', reminder.source.id]">
<i [c8yIcon]="'c8y-group-open'"></i>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,14 @@ aside {
display: flex;
flex-direction: column;

> div,
> small {
margin: 16px 0;
display: block;
padding: 0 16px;
}

> small {
display: block;
text-align: center;
}
}
Expand Down Expand Up @@ -157,6 +161,8 @@ aside {

footer {
margin: 4px 0 0;
display: flex;
flex-direction: column;
}

&.text-muted::before {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import { Component, OnDestroy } from '@angular/core';
import { AlertService, HeaderService } from '@c8y/ngx-components';
import { has } from 'lodash';
import { BsModalService } from 'ngx-bootstrap/modal';
import { BehaviorSubject, Subscription } from 'rxjs';
import {
Reminder,
ReminderGroup,
ReminderGroupFilter,
ReminderGroupStatus,
ReminderStatus,
ReminderType,
REMINDER_DRAWER_OPEN_CLASS,
REMINDER_MAIN_HEADER_CLASS,
REMINDER_TYPE_FRAGMENT,
} from '../../reminder.model';
import { ReminderService } from '../../services/reminder.service';
import { ReminderModalComponent } from '../reminder-modal/reminder-modal.component';
Expand All @@ -23,11 +27,13 @@ export class ReminderDrawerComponent implements OnDestroy {
reminders: Reminder[] = [];
reminderGroups: ReminderGroup[] = [];
lastUpdate?: Date;
types: ReminderType[] = [];

// for template
reminderStatus = ReminderStatus;
reminderGroupStatus = ReminderGroupStatus;
groupIsExpanded: boolean[] = [true, true, false];
typeFilter = '';

get open(): boolean {
return this._open;
Expand All @@ -48,7 +54,9 @@ export class ReminderDrawerComponent implements OnDestroy {
private reminderService: ReminderService,
private alertService: AlertService,
private modalService: BsModalService
) {
) {
this.initFilter();

// check if the actual drawer was opened
this.subscriptions.add(
this.headerService.rightDrawerOpen$.subscribe((open) => {
Expand All @@ -61,6 +69,7 @@ export class ReminderDrawerComponent implements OnDestroy {
})
);

// get live updates on reminders from service
this.subscriptions.add(
this.reminderService.reminders$.subscribe((reminders) =>
this.digestReminders(reminders)
Expand Down Expand Up @@ -103,6 +112,21 @@ export class ReminderDrawerComponent implements OnDestroy {
}
}

setTypeFilter(type: ReminderType['id']): void {
if (!this.types.length) return;

this.typeFilter = type;
this.filterReminders();
}

filterReminders(): void {
this.reminderGroups = this.reminderService.groupReminders(
this.reminders,
this.buildFilter()
);
this.reminderService.storeFilterConfig();
}

private toggleRightDrawer(open: boolean): void {
const drawer = document.getElementsByClassName(
REMINDER_MAIN_HEADER_CLASS
Expand All @@ -122,10 +146,35 @@ export class ReminderDrawerComponent implements OnDestroy {
}

private digestReminders(reminders: Reminder[]): void {
// TODO allow filtering in UI?
// - filter by type, group / device?
this.reminders = reminders;
this.reminderGroups = this.reminderService.groupReminders(reminders);
this.lastUpdate = new Date();
this.reminderGroups = this.reminderService.groupReminders(
reminders,
this.buildFilter()
);
}

private buildFilter(): ReminderGroupFilter {
const filters: ReminderGroupFilter = {};

// populate filters
if (this.typeFilter !== '')
filters[REMINDER_TYPE_FRAGMENT] = this.typeFilter;

return Object.keys(filters).length > 0 ? filters : null;
}

private initFilter(): void {
this.types = this.reminderService.types;

if (!this.types.length) {
this.reminderService.resetFilterConfig();
return;
}

const filters = this.reminderService.filters;

if (has(filters, REMINDER_TYPE_FRAGMENT))
this.typeFilter = filters[REMINDER_TYPE_FRAGMENT];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,27 @@ import { BsModalRef } from 'ngx-bootstrap/modal';
import {
Reminder,
ReminderStatus,
ReminderType,
REMINDER_TEXT_LENGTH,
REMINDER_TYPE,
REMINDER_TYPE_FRAGMENT,
} from '../../reminder.model';
import { ReminderService } from '../../services';

interface FormlySelectOptions {
label: string;
value: string;
group?: string;
}

@Component({
selector: 'c8y-reminder-modal',
templateUrl: './reminder-modal.component.html',
})
export class ReminderModalComponent implements OnInit {
isLoading = false;
asset!: Partial<IManagedObject>;
typeOptions!: FormlySelectOptions[];
isLoading = false;
form = new FormGroup({});

reminder: Partial<Reminder> = {
Expand Down Expand Up @@ -70,8 +80,11 @@ export class ReminderModalComponent implements OnInit {
private eventService: EventService,
private alertService: AlertService,
private activatedRoute: ActivatedRoute,
private translateService: TranslateService
) {}
private translateService: TranslateService,
private reminderService: ReminderService
) {
this.setTypeField();
}

ngOnInit(): void {
const asset = this.getAssetFromRoute(this.activatedRoute.snapshot);
Expand All @@ -94,6 +107,7 @@ export class ReminderModalComponent implements OnInit {
const reminder: IEvent = {
source: this.reminder.source,
type: REMINDER_TYPE,
reminderType: this.reminder.reminderType || null,
time: moment(this.reminder.time).seconds(0).toISOString(),
text: this.reminder.text,
status: ReminderStatus.active,
Expand Down Expand Up @@ -159,4 +173,23 @@ export class ReminderModalComponent implements OnInit {

return undefined;
}

private setTypeField(): void {
this.typeOptions = this.reminderService.types.map((type: ReminderType) => ({
label: type.name,
value: type.id,
}));

if (!this.typeOptions.length) return;

this.fields.push({
key: REMINDER_TYPE_FRAGMENT,
type: 'select',
props: {
label: this.translateService.instant('Reminder type (optional)'),
hidden: this.typeOptions?.length > 0,
options: this.typeOptions,
},
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{{ type?.name || 'Unknown' }}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
:host {
display: inline-block;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Component, Input } from '@angular/core';
import { Reminder, ReminderType } from '../../reminder.model';
import { ReminderService } from '../../services';

@Component({
selector: 'c8y-reminder-type',
templateUrl: './reminder-type.component.html',
styleUrl: './reminder-type.component.less',
})
export class ReminderTypeComponent {
@Input() set reminder(reminder: Reminder) {
this.setType(reminder.reminderType);
}

@Input() set id(reminderTypeID: ReminderType['id']) {
this.setType(reminderTypeID);
}

type: ReminderType;

constructor(private reminderService: ReminderService) {}

private setType(id: ReminderType['id']) {
this.type = {
id,
name: this.reminderService.getReminderTypeName(id),
};
}
}
2 changes: 2 additions & 0 deletions src/app/reminder-plugin/reminder-plugin.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ import {
ReminderModalComponent,
TimeFieldType,
} from './components';
import { ReminderTypeComponent } from './components/reminder-type/reminder-type.component';
import { DomService, ReminderService } from './services';

@NgModule({
declarations: [
ReminderIndicatorComponent,
ReminderDrawerComponent,
ReminderModalComponent,
ReminderTypeComponent,
AssetFieldType,
TimeFieldType,
],
Expand Down
17 changes: 16 additions & 1 deletion src/app/reminder-plugin/reminder.model.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import { IEvent } from '@c8y/client';
import { IEvent, ITenantOption } from '@c8y/client';

export const REMINDER_TYPE = 'c8y_Reminder';
export const REMINDER_TYPE_FRAGMENT = 'reminderType';
export const REMINDER_INITIAL_QUERY_SIZE = 100;
export const REMINDER_DRAWER_OPEN_CLASS = 'drawerOpen';
export const REMINDER_MAIN_HEADER_CLASS = 'app-main-header';
export const REMINDER_MAX_COUNTER = 10;
export const REMINDER_TEXT_LENGTH = 100;
export const REMINDER_TENENAT_OPTION_CATEGORY: ITenantOption['category'] = 'c8y.reminder';
export const REMINDER_TENENAT_OPTION_TYPE_KEY: ITenantOption['key'] = 'types';
export const REMINDER_LOCAL_STORAGE_FILTER = 'c8y_rpFilter';

export const ReminderGroupStatus = {
due: 'DUE',
Expand All @@ -26,10 +30,21 @@ export interface Reminder extends IEvent {
isGroup?: object;
diff?: number;
isCleared?: object;
reminderType?: ReminderType['id'];
}

export interface ReminderGroup {
status: ReminderGroupStatus;
reminders: Reminder[];
count: number;
total?: number;
}

export interface ReminderType {
id: string;
name: string;
}

export interface ReminderGroupFilter {
[key: string]: string;
}
Loading
Loading