Rename: Taggable -> Thing

This commit is contained in:
Patrick Haßel 2024-11-28 15:21:09 +01:00
parent b6f3db79e4
commit 8133080e9c
33 changed files with 178 additions and 213 deletions

View File

@ -1,18 +1,19 @@
import {Property} from "../Property/Property";
import {orNull, validateString} from "../api/validators";
import {Area} from '../Area/Area';
import {Thing} from '../Thing/Thing';
export class Device {
export class Device extends Thing {
constructor(
readonly area: Area,
readonly uuid: string,
readonly name: string,
readonly slug: string,
area: Area,
uuid: string,
name: string,
slug: string,
readonly statePropertyId: string,
readonly stateProperty: Property | null,
) {
//
super(area, uuid, name, slug);
}
static fromJson(json: any): Device {
@ -26,30 +27,4 @@ export class Device {
);
}
get nameOrArea(): string {
if (this.name === '') {
return this.area.name;
}
return this.name;
}
get nameWithArea(): string {
if (this.name === '') {
return this.area.name;
}
return this.area.name + ' ' + this.name;
}
static trackBy(index: number, device: Device) {
return device.uuid;
}
static compareByAreaThenName(a: Device, b: Device): number {
const area = Area.compareByName(a.area, b.area);
if (area !== 0) {
return area;
}
return a.name.localeCompare(b.name);
}
}

View File

@ -2,18 +2,19 @@ import {Property} from "../Property/Property";
import {orNull, validateString} from "../api/validators";
import {Area} from '../Area/Area';
import {Thing} from '../Thing/Thing';
export class Shutter {
export class Shutter extends Thing {
constructor(
readonly area: Area,
readonly uuid: string,
readonly name: string,
readonly slug: string,
area: Area,
uuid: string,
name: string,
slug: string,
readonly positionPropertyId: string,
readonly positionProperty: Property | null,
) {
//
super(area, uuid, name, slug);
}
static fromJson(json: any): Shutter {
@ -27,30 +28,4 @@ export class Shutter {
);
}
get nameOrArea(): string {
if (this.name === '') {
return this.area.name;
}
return this.name;
}
get nameWithArea(): string {
if (this.name === '') {
return this.area.name;
}
return this.area.name + ' ' + this.name;
}
static trackBy(index: number, shutter: Shutter) {
return shutter.uuid;
}
static compareByAreaThenName(a: Shutter, b: Shutter): number {
const area = Area.compareByName(a.area, b.area);
if (area !== 0) {
return area;
}
return a.name.localeCompare(b.name);
}
}

View File

@ -1,5 +0,0 @@
<div class="tileContainer taggableList">
<app-taggable-tile [now]="now" [taggable]="taggable" *ngFor="let taggable of list"></app-taggable-tile>
</div>

View File

@ -0,0 +1,41 @@
import {Area} from '../Area/Area';
export abstract class Thing {
protected constructor(
readonly area: Area,
readonly uuid: string,
readonly name: string,
readonly slug: string,
) {
//
}
get nameOrArea(): string {
if (this.name === '') {
return this.area.name;
}
return this.name;
}
get nameWithArea(): string {
if (this.name === '') {
return this.area.name;
}
return this.area.name + ' ' + this.name;
}
static trackBy(index: number, thing: Thing) {
return thing.uuid;
}
static compareByAreaThenName(a: Thing, b: Thing): number {
const area = Area.compareByName(a.area, b.area);
if (area !== 0) {
return area;
}
return a.name.localeCompare(b.name);
}
}

View File

@ -1,4 +1,4 @@
export class TaggableFilter {
export class ThingFilter {
tag: string = "";

View File

@ -3,6 +3,6 @@
<app-search [(search)]="filter.search" (doSearch)="liveList.refresh()"></app-search>
</div>
<div class="flexBoxRest verticalScroll">
<app-taggable-list [list]="liveList.list"></app-taggable-list>
<app-thing-list [list]="liveList.list"></app-thing-list>
</div>
</div>

View File

@ -1,46 +1,46 @@
import {Component, OnDestroy, OnInit} from '@angular/core';
import {TaggableListComponent} from '../taggable-list/taggable-list.component';
import {Taggable} from '../Taggable';
import {TaggableService} from '../taggable.service';
import {ThingListComponent} from '../thing-list/thing-list.component';
import {Thing} from '../Thing';
import {ThingService} from '../thing.service';
import {FormsModule} from '@angular/forms';
import {Subscription} from 'rxjs';
import {TaggableFilter} from '../TaggableFilter';
import {ThingFilter} from '../ThingFilter';
import {ActivatedRoute} from '@angular/router';
import {CrudLiveList} from '../../api/CrudLiveList';
import {SearchComponent} from '../../shared/search/search.component';
@Component({
selector: 'app-taggable-list-page',
selector: 'app-thing-list-page',
standalone: true,
imports: [
TaggableListComponent,
ThingListComponent,
FormsModule,
SearchComponent
],
templateUrl: './taggable-list-page.component.html',
styleUrl: './taggable-list-page.component.less'
templateUrl: './thing-list-page.component.html',
styleUrl: './thing-list-page.component.less'
})
export class TaggableListPageComponent implements OnInit, OnDestroy {
export class ThingListPageComponent implements OnInit, OnDestroy {
private readonly subs: Subscription[] = [];
protected readonly filter: TaggableFilter = new TaggableFilter();
protected readonly filter: ThingFilter = new ThingFilter();
protected readonly liveList: CrudLiveList<Taggable>;
protected readonly liveList: CrudLiveList<Thing>;
private tagSet: boolean = false;
constructor(
protected readonly taggableService: TaggableService,
protected readonly thingService: ThingService,
protected readonly activatedRoute: ActivatedRoute,
) {
this.subs.push(this.liveList = new CrudLiveList(
this.taggableService,
this.thingService,
false,
undefined,
next => {
if (this.tagSet) {
this.taggableService.filter(this.filter, next);
this.thingService.filter(this.filter, next);
} else {
next([]);
}
@ -53,7 +53,6 @@ export class TaggableListPageComponent implements OnInit, OnDestroy {
this.tagSet = 'tag' in params;
if (this.tagSet) {
this.filter.tag = params['tag'] || '';
console.log(this.filter.tag);
this.liveList.refresh();
} else {
this.liveList.clear();

View File

@ -0,0 +1,5 @@
<div class="tileContainer">
<app-thing-tile [now]="now" [thing]="thing" *ngFor="let thing of sorted()"></app-thing-tile>
</div>

View File

@ -1,27 +1,27 @@
import {Component, Input, OnDestroy, OnInit} from '@angular/core';
import {NgForOf} from '@angular/common';
import {Subscription, timer} from 'rxjs';
import {TaggableTileComponent} from '../taggable-tile/taggable-tile.component';
import {Taggable} from '../Taggable';
import {ThingTileComponent} from '../thing-tile/thing-tile.component';
import {Thing} from '../Thing';
@Component({
selector: 'app-taggable-list',
selector: 'app-thing-list',
standalone: true,
imports: [
NgForOf,
TaggableTileComponent
ThingTileComponent
],
templateUrl: './taggable-list.component.html',
styleUrl: './taggable-list.component.less'
templateUrl: './thing-list.component.html',
styleUrl: './thing-list.component.less'
})
export class TaggableListComponent implements OnInit, OnDestroy {
export class ThingListComponent implements OnInit, OnDestroy {
private readonly subs: Subscription[] = [];
protected now: Date = new Date();
@Input()
list: Taggable[] = [];
list: Thing[] = [];
ngOnInit(): void {
this.now = new Date();
@ -32,4 +32,8 @@ export class TaggableListComponent implements OnInit, OnDestroy {
this.subs.forEach(sub => sub.unsubscribe());
}
sorted(): Thing[] {
return this.list.sort(Thing.compareByAreaThenName);
}
}

View File

@ -6,10 +6,10 @@ import {DeviceTileComponent} from '../../Device/device-tile/device-tile.componen
import {NgIf} from '@angular/common';
import {ShutterTileComponent} from '../../Shutter/shutter-tile/shutter-tile.component';
import {TunableTileComponent} from '../../Tunable/tunable-tile/tunable-tile.component';
import {Taggable} from '../Taggable';
import {Thing} from '../Thing';
@Component({
selector: 'app-taggable-tile',
selector: 'app-thing-tile',
standalone: true,
imports: [
DeviceTileComponent,
@ -17,39 +17,39 @@ import {Taggable} from '../Taggable';
ShutterTileComponent,
TunableTileComponent
],
templateUrl: './taggable-tile.component.html',
styleUrl: './taggable-tile.component.less'
templateUrl: './thing-tile.component.html',
styleUrl: './thing-tile.component.less'
})
export class TaggableTileComponent {
export class ThingTileComponent {
@Input()
now!: Date;
@Input()
taggable!: Taggable;
thing!: Thing;
asDevice(): Device {
return this.taggable as Device;
return this.thing as Device;
}
isDevice(): boolean {
return this.taggable instanceof Device;
return this.thing instanceof Device;
}
asShutter(): Shutter {
return this.taggable as Shutter;
return this.thing as Shutter;
}
isShutter(): boolean {
return this.taggable instanceof Shutter;
return this.thing instanceof Shutter;
}
asTunable(): Tunable {
return this.taggable as Tunable;
return this.thing as Tunable;
}
isTunable(): boolean {
return this.taggable instanceof Tunable;
return this.thing instanceof Tunable;
}
}

View File

@ -1,17 +1,18 @@
import {Injectable} from '@angular/core';
import {ApiService} from '../api/api.service';
import {CrudService} from '../api/CrudService';
import {Taggable, taggableFromJson} from './Taggable';
import {Thing} from './Thing';
import {Next} from '../api/types';
import {Subject, Subscription} from 'rxjs';
import {DeviceService} from '../Device/device.service';
import {ShutterService} from '../Shutter/shutter.service';
import {TunableService} from '../Tunable/tunable.service';
import {thingFromJson} from './thingFromJson';
@Injectable({
providedIn: 'root'
})
export class TaggableService extends CrudService<Taggable> {
export class ThingService extends CrudService<Thing> {
constructor(
apiService: ApiService,
@ -19,11 +20,11 @@ export class TaggableService extends CrudService<Taggable> {
protected readonly shutterService: ShutterService,
protected readonly tunableService: TunableService,
) {
super(apiService, ['Taggable'], taggableFromJson);
super(apiService, ['Thing'], thingFromJson);
}
override subscribe(next: Next<Taggable>): Subscription {
const subject = new Subject<Taggable>();
override subscribe(next: Next<Thing>): Subscription {
const subject = new Subject<Thing>();
this.deviceService.subscribe(next => subject.next(next));
this.shutterService.subscribe(next => subject.next(next));
this.tunableService.subscribe(next => subject.next(next));

View File

@ -1,11 +1,10 @@
import {validateAndRemoveDtoSuffix} from "../api/validators";
import {Device} from "../Device/Device";
import {Shutter} from "../Shutter/Shutter";
import {Tunable} from "../Tunable/Tunable";
import {validateAndRemoveDtoSuffix} from "../api/validators";
import {Thing} from "./Thing";
export type Taggable = Device | Shutter | Tunable;
export function taggableFromJson(json: any): Taggable {
export function thingFromJson(json: any): Thing {
const _type_ = validateAndRemoveDtoSuffix(json._type_);
switch (_type_) {
case 'Device':

View File

@ -1,14 +1,15 @@
import {Property} from "../Property/Property";
import {orNull, validateString} from "../api/validators";
import {Area} from '../Area/Area';
import {Thing} from '../Thing/Thing';
export class Tunable {
export class Tunable extends Thing {
constructor(
readonly area: Area,
readonly uuid: string,
readonly name: string,
readonly slug: string,
area: Area,
uuid: string,
name: string,
slug: string,
readonly statePropertyId: string,
readonly stateProperty: Property | null,
readonly brightnessPropertyId: string,
@ -16,7 +17,7 @@ export class Tunable {
readonly coldnessPropertyId: string,
readonly coldnessProperty: Property | null,
) {
//
super(area, uuid, name, slug);
}
static fromJson(json: any): Tunable {
@ -34,30 +35,4 @@ export class Tunable {
);
}
get nameOrArea(): string {
if (this.name === '') {
return this.area.name;
}
return this.name;
}
get nameWithArea(): string {
if (this.name === '') {
return this.area.name;
}
return this.area.name + ' ' + this.name;
}
static trackBy(index: number, tunable: Tunable) {
return tunable.uuid;
}
static compareByAreaThenName(a: Tunable, b: Tunable): number {
const area = Area.compareByName(a.area, b.area);
if (area !== 0) {
return area;
}
return a.name.localeCompare(b.name);
}
}

View File

@ -2,7 +2,7 @@ import {CrudService} from "./CrudService";
import {Subscription} from "rxjs";
import {Next} from './types';
export interface UUID {
interface UUID {
uuid: string;

View File

@ -1,9 +1,9 @@
<div class="flexBox">
<div class="flexBoxFixed menu">
<div class="item itemLeft" routerLink="Dashboard" routerLinkActive="active">Dash</div>
<div class="item itemLeft" routerLink="TaggableList/device" routerLinkActive="active">Geräte</div>
<div class="item itemLeft" routerLink="TaggableList/light" routerLinkActive="active">Licht</div>
<div class="item itemLeft" routerLink="TaggableList/shutter" routerLinkActive="active">Rollladen</div>
<div class="item itemLeft" routerLink="ThingList/device" routerLinkActive="active">Geräte</div>
<div class="item itemLeft" routerLink="ThingList/light" routerLinkActive="active">Licht</div>
<div class="item itemLeft" routerLink="ThingList/shutter" routerLinkActive="active">Rollladen</div>
<div class="item itemRight" routerLink="GroupList" routerLinkActive="active">KNX</div>
</div>
<div class="flexBoxRest">

View File

@ -1,12 +1,12 @@
import {Routes} from '@angular/router';
import {KnxGroupListPageComponent} from './Group/knx-group-list-page/knx-group-list-page.component';
import {DashboardComponent} from './dashboard/dashboard.component';
import {TaggableListPageComponent} from './Taggable/taggable-list-page/taggable-list-page.component';
import {ThingListPageComponent} from './Thing/thing-list-page/thing-list-page.component';
export const routes: Routes = [
{path: 'Dashboard', component: DashboardComponent},
{path: 'GroupList', component: KnxGroupListPageComponent},
{path: 'TaggableList', component: TaggableListPageComponent},
{path: 'TaggableList/:tag', component: TaggableListPageComponent},
{path: 'ThingList', component: ThingListPageComponent},
{path: 'ThingList/:tag', component: ThingListPageComponent},
{path: '**', redirectTo: 'Dashboard'},
];

View File

@ -1,7 +1,7 @@
package de.ph87.home.device;
import de.ph87.home.area.Area;
import de.ph87.home.tag.taggable.ITaggable;
import de.ph87.home.thing.IThing;
import de.ph87.home.tag.Tag;
import jakarta.persistence.*;
import lombok.*;
@ -16,7 +16,7 @@ import static de.ph87.home.common.ListHelpers.merge;
@Getter
@ToString
@NoArgsConstructor
public class Device implements ITaggable {
public class Device implements IThing {
@Id
@NonNull

View File

@ -8,9 +8,9 @@ import de.ph87.home.property.*;
import de.ph87.home.search.SearchableDto;
import de.ph87.home.tag.Tag;
import de.ph87.home.tag.TagReader;
import de.ph87.home.tag.TaggableDto;
import de.ph87.home.tag.taggable.ITaggableService;
import de.ph87.home.tag.taggable.TaggableFilter;
import de.ph87.home.thing.ThingDto;
import de.ph87.home.thing.IThingService;
import de.ph87.home.thing.ThingFilter;
import jakarta.annotation.Nullable;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
@ -28,7 +28,7 @@ import static de.ph87.home.common.crud.SearchHelper.search;
@Service
@Transactional
@RequiredArgsConstructor
public class DeviceService implements ITaggableService<DeviceDto> {
public class DeviceService implements IThingService<DeviceDto> {
private final PropertyService propertyService;
@ -104,12 +104,12 @@ public class DeviceService implements ITaggableService<DeviceDto> {
}
@Override
public List<TaggableDto<DeviceDto>> findTaggables(final @NonNull TaggableFilter filter) {
public List<ThingDto<DeviceDto>> filter(final @NonNull ThingFilter filter) {
return deviceRepository.findAll().stream()
.filter(device -> search(filter.getSearch(), device.getSearchableValues()))
.filter(device -> device.tagListAnyMatch(filter.getTag()))
.map(this::toDto)
.map(TaggableDto::new)
.map(ThingDto::new)
.toList();
}

View File

@ -1,7 +1,7 @@
package de.ph87.home.shutter;
import de.ph87.home.area.Area;
import de.ph87.home.tag.taggable.ITaggable;
import de.ph87.home.thing.IThing;
import de.ph87.home.tag.Tag;
import jakarta.persistence.*;
import lombok.*;
@ -16,7 +16,7 @@ import static de.ph87.home.common.ListHelpers.merge;
@Getter
@ToString
@NoArgsConstructor
public class Shutter implements ITaggable {
public class Shutter implements IThing {
@Id
@NonNull

View File

@ -8,9 +8,9 @@ import de.ph87.home.property.*;
import de.ph87.home.search.SearchableDto;
import de.ph87.home.tag.Tag;
import de.ph87.home.tag.TagReader;
import de.ph87.home.tag.TaggableDto;
import de.ph87.home.tag.taggable.ITaggableService;
import de.ph87.home.tag.taggable.TaggableFilter;
import de.ph87.home.thing.ThingDto;
import de.ph87.home.thing.IThingService;
import de.ph87.home.thing.ThingFilter;
import jakarta.annotation.Nullable;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
@ -28,7 +28,7 @@ import static de.ph87.home.common.crud.SearchHelper.search;
@Service
@Transactional
@RequiredArgsConstructor
public class ShutterService implements ITaggableService<ShutterDto> {
public class ShutterService implements IThingService<ShutterDto> {
private final AreaService areaService;
@ -110,12 +110,12 @@ public class ShutterService implements ITaggableService<ShutterDto> {
}
@Override
public List<TaggableDto<ShutterDto>> findTaggables(final @NonNull TaggableFilter filter) {
public List<ThingDto<ShutterDto>> filter(final @NonNull ThingFilter filter) {
return shutterRepository.findAll().stream()
.filter(shutter -> shutter.tagListAnyMatch(filter.getTag()))
.filter(device -> search(filter.getSearch(), device.getSearchableValues()))
.map(this::toDto)
.map(TaggableDto::new)
.map(ThingDto::new)
.toList();
}

View File

@ -1,13 +0,0 @@
package de.ph87.home.tag.taggable;
import de.ph87.home.search.ISearchableService;
import de.ph87.home.tag.TaggableDto;
import lombok.NonNull;
import java.util.List;
public interface ITaggableService<T> extends ISearchableService<T> {
List<TaggableDto<T>> findTaggables(final @NonNull TaggableFilter filter);
}

View File

@ -1,4 +1,4 @@
package de.ph87.home.tag.taggable;
package de.ph87.home.thing;
import de.ph87.home.search.ISearchable;
import de.ph87.home.tag.Tag;
@ -6,7 +6,7 @@ import lombok.NonNull;
import java.util.List;
public interface ITaggable extends ISearchable {
public interface IThing extends ISearchable {
List<Tag> getTagList();

View File

@ -0,0 +1,12 @@
package de.ph87.home.thing;
import de.ph87.home.search.ISearchableService;
import lombok.NonNull;
import java.util.List;
public interface IThingService<T> extends ISearchableService<T> {
List<ThingDto<T>> filter(@NonNull final ThingFilter filter);
}

View File

@ -1,7 +1,5 @@
package de.ph87.home.tag.taggable;
package de.ph87.home.thing;
import de.ph87.home.tag.TaggableDto;
import jakarta.annotation.Nullable;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
@ -13,14 +11,14 @@ import java.util.List;
@RestController
@RequiredArgsConstructor
@RequestMapping("Taggable")
public class TaggableController {
@RequestMapping("Thing")
public class ThingController {
private final TaggableService taggableService;
private final ThingService thingService;
@PostMapping(value = "list")
public List<? extends TaggableDto<?>> list(@RequestBody @NonNull final TaggableFilter filter) {
return taggableService.list(filter);
public List<? extends ThingDto<?>> list(@RequestBody @NonNull final ThingFilter filter) {
return thingService.list(filter);
}
}

View File

@ -1,15 +1,15 @@
package de.ph87.home.tag;
package de.ph87.home.thing;
import lombok.Data;
@Data
public class TaggableDto<T> {
public class ThingDto<T> {
private final String _type_;
private final T payload;
public TaggableDto(final T payload) {
public ThingDto(final T payload) {
this.payload = payload;
this._type_ = payload.getClass().getSimpleName();
}

View File

@ -1,10 +1,10 @@
package de.ph87.home.tag.taggable;
package de.ph87.home.thing;
import lombok.Data;
import lombok.NonNull;
@Data
public class TaggableFilter {
public class ThingFilter {
@NonNull
private final String tag;

View File

@ -1,7 +1,6 @@
package de.ph87.home.tag.taggable;
package de.ph87.home.thing;
import de.ph87.home.common.ListHelpers;
import de.ph87.home.tag.TaggableDto;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -15,14 +14,14 @@ import java.util.List;
@Service
@Transactional
@RequiredArgsConstructor
public class TaggableService {
public class ThingService {
private final List<ITaggableService<?>> taggableServices;
private final List<IThingService<?>> thingServices;
@NonNull
public List<? extends TaggableDto<?>> list(@NonNull final TaggableFilter filter) {
return taggableServices.stream()
.map(iTaggableService -> iTaggableService.findTaggables(filter))
public List<? extends ThingDto<?>> list(@NonNull final ThingFilter filter) {
return thingServices.stream()
.map(iThingService -> iThingService.filter(filter))
.reduce(ListHelpers::merge)
.orElse(new ArrayList<>());
}

View File

@ -1,7 +1,7 @@
package de.ph87.home.tunable;
import de.ph87.home.area.Area;
import de.ph87.home.tag.taggable.ITaggable;
import de.ph87.home.thing.IThing;
import de.ph87.home.tag.Tag;
import jakarta.persistence.*;
import lombok.*;
@ -16,7 +16,7 @@ import static de.ph87.home.common.ListHelpers.merge;
@Getter
@ToString
@NoArgsConstructor
public class Tunable implements ITaggable {
public class Tunable implements IThing {
@Id
@NonNull

View File

@ -8,9 +8,9 @@ import de.ph87.home.property.*;
import de.ph87.home.search.SearchableDto;
import de.ph87.home.tag.Tag;
import de.ph87.home.tag.TagReader;
import de.ph87.home.tag.TaggableDto;
import de.ph87.home.tag.taggable.ITaggableService;
import de.ph87.home.tag.taggable.TaggableFilter;
import de.ph87.home.thing.ThingDto;
import de.ph87.home.thing.IThingService;
import de.ph87.home.thing.ThingFilter;
import jakarta.annotation.Nullable;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
@ -28,7 +28,7 @@ import static de.ph87.home.common.crud.SearchHelper.search;
@Service
@Transactional
@RequiredArgsConstructor
public class TunableService implements ITaggableService<TunableDto> {
public class TunableService implements IThingService<TunableDto> {
private final PropertyService propertyService;
@ -132,12 +132,12 @@ public class TunableService implements ITaggableService<TunableDto> {
}
@Override
public List<TaggableDto<TunableDto>> findTaggables(final @NonNull TaggableFilter filter) {
public List<ThingDto<TunableDto>> filter(final @NonNull ThingFilter filter) {
return tunableRepository.findAll().stream()
.filter(tunable -> tunable.tagListAnyMatch(filter.getTag()))
.filter(device -> search(filter.getSearch(), device.getSearchableValues()))
.map(this::toDto)
.map(TaggableDto::new)
.map(ThingDto::new)
.toList();
}