Update pastoral domain

This commit is contained in:
Chris Chen 2022-10-02 08:39:35 -07:00
parent 911b45739d
commit 06cb6683e6
28 changed files with 438 additions and 351 deletions

View File

@ -4,7 +4,6 @@ import { NbMenuItem } from '@nebular/theme';
import { AdminComponent } from './admin.component';
import { FamilyMembersComponent } from './family-members/family-members.component';
import { HappinessGroupsComponent } from './happiness-groups/happiness-groups.component';
import { LineMessagingAccountComponent } from './lines/line-messaging-account/line-messaging-account.component';
import { LogsComponent } from './logs/logs.component';
import { PastoralDomainsComponent } from './pastoral-domains/pastoral-domains.component';
@ -55,7 +54,7 @@ const routes: Routes = [
[
{ path: 'members', component: FamilyMembersComponent },
{ path: 'pastoralDomains', component: PastoralDomainsComponent },
{ path: 'happinessGroup', component: HappinessGroupsComponent },
{ path: 'happinessGroup', component: PastoralDomainsComponent },
{ path: 'logs', component: LogsComponent },
{ path: 'lineAccount', component: LineMessagingAccountComponent },
]

View File

@ -3,10 +3,8 @@ import { CommonModule } from '@angular/common';
import { AdminRoutingModule } from './admin-routing.module';
import { AdminComponent } from './admin.component';
import { HappinessGroupsComponent } from './happiness-groups/happiness-groups.component';
import { NbMenuModule, NbInputModule, NbCardModule, NbButtonModule, NbActionsModule, NbCheckboxModule, NbRadioModule, NbDatepickerModule, NbSelectModule, NbIconModule, NbTagModule, NbStepperModule, NbListModule, NbSpinnerModule, NbDialogModule, NbUserModule } from '@nebular/theme';
import { ThemeModule } from '../@theme/theme.module';
import { HappinessGroupAddingDlgComponent } from './happiness-groups/happiness-group-adding-dlg/happiness-group-adding-dlg.component';
import { FormsModule } from '@angular/forms';
import { BestListDlgComponent } from './happiness-groups/best-list-dlg/best-list-dlg.component';
import { AlertDlgModule } from '../ui/alert-dlg/alert-dlg.module';
@ -28,12 +26,11 @@ import { MaskDirectiveModule } from '../directives/mask-directive/mask-directive
import { DateInputModule } from '../ui/date-input/date-input.module';
import { LineMessagingAccountComponent } from './lines/line-messaging-account/line-messaging-account.component';
import { LineMessagingAccountEditorComponent } from './lines/line-messaging-account-editor/line-messaging-account-editor.component';
import { WeekTaskEditorComponent } from './happiness-groups/week-task-editor/week-task-editor.component';
@NgModule({
declarations: [
AdminComponent,
HappinessGroupsComponent,
HappinessGroupAddingDlgComponent,
BestListDlgComponent,
HappinessWeekEditorComponent,
HappinessWeekListDlgComponent,
@ -46,7 +43,8 @@ import { LineMessagingAccountEditorComponent } from './lines/line-messaging-acco
LogsComponent,
LogDetailComponent,
LineMessagingAccountComponent,
LineMessagingAccountEditorComponent],
LineMessagingAccountEditorComponent,
WeekTaskEditorComponent],
imports: [
CommonModule,
FormsModule,

View File

@ -9,7 +9,7 @@
<nb-card-body [nbSpinner]="processing">
<nb-list>
<nb-list-item *ngFor="let best of group.bestList" class="d-block">
<nb-list-item *ngFor="let best of group.bests" class="d-block">
<div class="row">
<div class="col-md-4">

View File

@ -2,7 +2,8 @@ import { Component, OnInit } from '@angular/core';
import { NbDialogRef, NbToastrService } from '@nebular/theme';
import { first } from 'rxjs/operators';
import { environment } from '../../../../environments/environment';
import { HappinessBEST, HappinessGroup } from '../../../entity/HappinessGroup';
import { HappinessBEST } from '../../../entity/HappinessGroup';
import { PastoralDomain } from '../../../entity/PastoralDomain';
import { BestService } from '../../../services/crudServices/best.service';
import { HappinessGroupService } from '../../../services/crudServices/happiness-group.service';
import { LineGroup, LineService } from '../../../services/line.service';
@ -20,7 +21,7 @@ export class BestListDlgComponent implements OnInit {
processing: boolean = false;
isAdding: boolean = false;
group: HappinessGroup;
group: PastoralDomain;
datum: HappinessBEST;
constructor(
private dlgRef: NbDialogRef<BestListDlgComponent>,
@ -32,7 +33,7 @@ export class BestListDlgComponent implements OnInit {
) { }
ngOnInit(): void {
this.group.bestList = this.group.bestList.sort((a, b) => 0 - (a.name < b.name ? 1 : -1));
this.group.bests = this.group.bests.sort((a, b) => 0 - (a.name < b.name ? 1 : -1));
}
close() {
@ -63,7 +64,7 @@ export class BestListDlgComponent implements OnInit {
this.toastrService.success(dummy.value, "複製地址至剪貼簿!");
}
copyBestList() {
let list = "".concat(...this.group.bestList.map(b => `${b.name}\t\t: ${environment.invitationUrl}${b.id}\r\n`));
let list = "".concat(...this.group.bests.map(b => `${b.name}\t\t: ${environment.invitationUrl}${b.id}\r\n`));
var dummy = document.createElement("textarea");
document.body.appendChild(dummy);
dummy.value = list;

View File

@ -1,54 +0,0 @@
<nb-card status="success">
<nb-card-header>
Happiness Group
</nb-card-header>
<nb-card-body>
<div class="row">
<div class="col-md-6">
<div class='form-group'>
<label for='name' class='label'>Name</label>
<input type='text' nbInput fullWidth id='name' name='name' [(ngModel)]='datum.name'>
</div>
</div>
<div class="col-md-6">
<div class='form-group'>
<label for='beginTime' class='label'>Begin Time</label>
<input type='text' nbInput fullWidth id='beginTime' name='beginTime' [(ngModel)]='datum.beginTime'
[nbDatepicker]="dateTimePicker">
<nb-date-timepicker format="yyyy/MM/dd HH:mm" #dateTimePicker></nb-date-timepicker>
<!-- <input nbInput placeholder="Pick Date" [nbDatepicker]="dateTimePicker">
<nb-date-timepicker #dateTimePicker format="yyyy/MM/dd HH:mm"></nb-date-timepicker>
<nb-datepicker #datepicker></nb-datepicker> -->
</div>
</div>
<div class="col-md-6">
<div class='form-group'>
<label for='address' class='label'>Address</label>
<input type='text' nbInput fullWidth id='address' name='address' [(ngModel)]='datum.address'>
</div>
</div>
<div class="col-md-6">
<div class='form-group'>
<label for='CityAndZipCode' class='label'>City And Zip code</label>
<input type='text' nbInput fullWidth id='CityAndZipCode' name='CityAndZipCode'
[(ngModel)]='datum.cityAndZipCode'>
</div>
</div>
<div class="col-12 col-md">
<div class='form-group'>
<label for='invitationText' class='label'>Invitation Text</label>
<textarea type='text' nbInput fullWidth id='invitationText' name='invitationText'
[(ngModel)]='datum.invitationText'></textarea>
</div>
</div>
</div>
</nb-card-body>
<nb-card-footer>
<button class="float-right" nbButton hero status="danger" size="small" (click)="close()"
[nbSpinner]="processing">Close</button>
<button class="float-right mr-2" nbButton hero status="primary" size="small" (click)="update()"
[nbSpinner]="processing">Submit</button>
</nb-card-footer>
</nb-card>

View File

@ -1,4 +0,0 @@
nb-card {
max-height: 90vh;
width: 700px;
}

View File

@ -1,25 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { HappinessGroupAddingDlgComponent } from './happiness-group-adding-dlg.component';
describe('HappinessGroupAddingDlgComponent', () => {
let component: HappinessGroupAddingDlgComponent;
let fixture: ComponentFixture<HappinessGroupAddingDlgComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ HappinessGroupAddingDlgComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(HappinessGroupAddingDlgComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -1,28 +0,0 @@
import { Component, OnInit } from '@angular/core';
import { NbDialogRef } from '@nebular/theme';
import { HappinessGroup } from '../../../entity/HappinessGroup';
@Component({
selector: 'ngx-happiness-group-adding-dlg',
templateUrl: './happiness-group-adding-dlg.component.html',
styleUrls: ['./happiness-group-adding-dlg.component.scss']
})
export class HappinessGroupAddingDlgComponent implements OnInit {
processing: boolean = false;
isAdding: boolean = false;
datum: HappinessGroup;
constructor(
private dlgRef: NbDialogRef<HappinessGroupAddingDlgComponent>
) { }
ngOnInit(): void {
}
close() {
this.dlgRef.close();
}
update() {
this.dlgRef.close(this.datum);
}
}

View File

@ -1,53 +0,0 @@
<nb-card status="success">
<nb-card-header>
Happiness Group
<button class="float-right" nbButton hero status="success" size="small" (click)="openAddDlg()"
[nbSpinner]="processing">Create New</button>
</nb-card-header>
<nb-card-body [nbSpinner]="processing">
<div class="table-responsive">
<table class="table table-striped table-inverse">
<thead class="thead-inverse">
<tr>
<th>Name</th>
<th>Begin Time</th>
<th></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let group of allData">
<td scope="row">
<nb-user [name]="group.name">
</nb-user>
</td>
<td>
{{formatDate(group.beginTime)}}
</td>
<td class="text-right">
<button nbButton hero status="primary" class="mr-3" size="small"
(click)="openEditDlg(group)">Edit</button>
<button nbButton hero status="primary" class="mr-3" size="small"
(click)="openWeekListDlg(group)">Manage Week Invitations</button>
<button nbButton hero status="primary" class="" size="small"
(click)="openBestListDlg(group)">Manage
Bests</button>
</td>
</tr>
</tbody>
</table>
</div>
</nb-card-body>
<nb-card-footer>
<!-- <button class="float-right" nbButton hero status="danger" size="small" (click)="close()"
[nbSpinner]="processing">Close</button>
<button class="float-right mr-2" nbButton hero status="primary" size="small" (click)="update()"
[nbSpinner]="processing" [disabled]="!allowSubmit">Submit</button> -->
</nb-card-footer>
</nb-card>

View File

@ -1,93 +0,0 @@
import { Component, OnInit } from '@angular/core';
import { NbDateService, NbDialogService } from '@nebular/theme';
import { first } from 'rxjs/operators';
import { HappinessGroup } from '../../entity/HappinessGroup';
import { HappinessGroupService } from '../../services/crudServices/happiness-group.service';
import { DateUtils } from '../../utilities/date-utils';
import { BestListDlgComponent } from './best-list-dlg/best-list-dlg.component';
import { HappinessGroupAddingDlgComponent } from './happiness-group-adding-dlg/happiness-group-adding-dlg.component';
import { HappinessWeekListDlgComponent } from './happiness-week-list-dlg/happiness-week-list-dlg.component';
@Component({
selector: 'ngx-happiness-groups',
templateUrl: './happiness-groups.component.html',
styleUrls: ['./happiness-groups.component.scss']
})
export class HappinessGroupsComponent implements OnInit {
constructor(private happinessService: HappinessGroupService,
private dlgService: NbDialogService,
) { }
allData: HappinessGroup[] = [];
processing: boolean = false;
ngOnInit(): void {
this.getAllData();
}
getAllData() {
this.processing = true;
this.happinessService.getAll().pipe(first()).subscribe(result => {
this.allData = result;
this.processing = false;
});
}
openEditDlg(group: HappinessGroup) {
this.dlgService.open(HappinessGroupAddingDlgComponent, {
context: {
isAdding: false,
datum: group
}
}).onClose.pipe(first()).subscribe(result => {
if (result) {
this.processing = true;
this.happinessService.update(result).pipe(first()).subscribe(result => {
this.getAllData();
});
}
});
}
openAddDlg() {
this.dlgService.open(HappinessGroupAddingDlgComponent, {
context: {
isAdding: true,
datum: { id: '123', beginTime: new Date() } as HappinessGroup
}
}).onClose.pipe(first()).subscribe(result => {
if (result) {
this.processing = true;
this.happinessService.createOrUpdate(result).pipe(first()).subscribe(result => {
this.getAllData();
});
}
});
}
openBestListDlg(currentGroup: HappinessGroup) {
this.dlgService.open(BestListDlgComponent, {
context: {
group: currentGroup
}
}).onClose.pipe(first()).subscribe(result => {
this.getAllData();
});
}
openWeekListDlg(currentGroup: HappinessGroup) {
this.dlgService.open(HappinessWeekListDlgComponent, {
context: {
group: currentGroup
}
}).onClose.pipe(first()).subscribe(result => {
this.getAllData();
});
}
formatDate(d: Date) {
return DateUtils.format(d, "yyyy/MM/dd hh:mm aa");
}
}

View File

@ -5,12 +5,12 @@
<nb-card-body>
<nb-list>
<nb-list-item *ngFor="let week of group.weeks" class="d-block">
<nb-list-item *ngFor="let week of group.happinessWeeks" class="d-block">
<div class="row">
<div class="col-md-4">
<nb-user [name]="weekDisplay(week.seq)">
</nb-user>
<div class="col-md-6">
{{weekDisplay(week.seq)}}
<!-- <nb-user [name]="weekDisplay(week.seq)">
</nb-user> -->
</div>
@ -18,6 +18,9 @@
<button nbButton hero status="primary" class="mr-1" size="small"
(click)="openEditDlg(week)">Edit</button>
<button nbButton hero status="primary" class="mr-1" size="small"
(click)="openTaskDlg(week)">Tasks</button>
</div>
</div>

View File

@ -0,0 +1,4 @@
nb-card {
max-height: 90vh;
width: 700px;
}

View File

@ -1,9 +1,11 @@
import { Component, OnInit } from '@angular/core';
import { NbDialogRef, NbDialogService } from '@nebular/theme';
import { first } from 'rxjs/operators';
import { HappinessGroup, HappinessWeek } from '../../../entity/HappinessGroup';
import { HappinessWeek } from '../../../entity/HappinessGroup';
import { PastoralDomain } from '../../../entity/PastoralDomain';
import { NumberUtils } from '../../../utilities/number-utils';
import { HappinessWeekEditorComponent } from '../happiness-week-editor/happiness-week-editor.component';
import { WeekTaskEditorComponent } from '../week-task-editor/week-task-editor.component';
@Component({
selector: 'ngx-happiness-week-list-dlg',
@ -12,7 +14,7 @@ import { HappinessWeekEditorComponent } from '../happiness-week-editor/happiness
})
export class HappinessWeekListDlgComponent implements OnInit {
group: HappinessGroup;
group: PastoralDomain;
constructor(
private dlgRef: NbDialogRef<any>,
private dlgService: NbDialogService
@ -31,9 +33,20 @@ export class HappinessWeekListDlgComponent implements OnInit {
}
}).onClose.pipe(first()).subscribe(result => {
if (result) this.close();
});;
});
}
openTaskDlg(week: HappinessWeek) {
this.dlgService.open(WeekTaskEditorComponent, {
context: {
data: week,
allData: this.group.happinessWeeks
}
}).onClose.pipe(first()).subscribe(result => {
});
}
weekDisplay(seq: number) {
return NumberUtils.Ordinal(seq) + ' Week';
return `W${seq} ${this.group.happinessWeeks[seq - 1].topic}`;
}
}

View File

@ -0,0 +1,38 @@
<form #form="ngForm">
<nb-card>
<nb-card-header>
第 {{data.seq}} 周 {{data.topic}} 分工
</nb-card-header>
<nb-card-body *ngIf="data">
<div class="row" *ngFor="let task of data.tasks;let i = index">
<div class="col-md-4">
<div class='form-group'>
<label for='tasker{{i}}' class='label'>{{getTaskTitle(task.type)}}</label>
<op-drop-down name="tasker{{i}}" [editable]="true" [(ngModel)]="task.tasker"
[source]="taskrOptions">
</op-drop-down>
</div>
</div>
<div class="col-md-8">
<div class="form-group">
<label for="content{{i}}" class="label">內容</label>
<input type="text" name="content{{i}}" nbInput fullWidth id="content{{i}}"
[(ngModel)]="task.content">
</div>
</div>
</div>
</nb-card-body>
<nb-card-footer>
<button class="float-right" nbButton hero status="danger" size="small" (click)="close()"
[nbSpinner]="processing">
Close
</button>
<button class="float-right mr-2" nbButton hero status="primary" size="small"
(click)="!form.invalid&&!processing&&update()" [disabled]="form.invalid" [nbSpinner]="processing">
Save
</button>
</nb-card-footer>
</nb-card>
</form>

View File

@ -1,20 +1,20 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { HappinessGroupsComponent } from './happiness-groups.component';
import { WeekTaskEditorComponent } from './week-task-editor.component';
describe('HappinessGroupsComponent', () => {
let component: HappinessGroupsComponent;
let fixture: ComponentFixture<HappinessGroupsComponent>;
describe('WeekTaskEditorComponent', () => {
let component: WeekTaskEditorComponent;
let fixture: ComponentFixture<WeekTaskEditorComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ HappinessGroupsComponent ]
declarations: [ WeekTaskEditorComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(HappinessGroupsComponent);
fixture = TestBed.createComponent(WeekTaskEditorComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

View File

@ -0,0 +1,79 @@
import { Component, OnInit } from '@angular/core';
import { NbDialogRef } from '@nebular/theme';
import { HappinessTask, HappinessTaskType, HappinessWeek } from '../../../entity/HappinessGroup';
import { HappinessTaskService } from '../../../services/crudServices/happiness-group.service';
import { first } from 'rxjs/operators';
import { DropDownOption } from '../../../entity/dropDownOption';
import { StringUtils } from '../../../utilities/string-utils';
@Component({
selector: 'ngx-week-task-editor',
templateUrl: './week-task-editor.component.html',
styleUrls: ['./week-task-editor.component.scss']
})
export class WeekTaskEditorComponent implements OnInit {
processing: boolean = false;
allData: HappinessWeek[];
data: HappinessWeek;
taskrOptions: DropDownOption[] = [];
constructor(
private happinessTaskService: HappinessTaskService,
private dlgRef: NbDialogRef<WeekTaskEditorComponent>
) {
}
ngOnInit(): void {
if (this.data.tasks.length == 0) {
this.data.tasks = [];
for (let i = HappinessTaskType.IceBreak; i <= HappinessTaskType.Dessert; i++) {
this.data.tasks.push(
{
weekId: this.data.id,
id: 'new',
type: i as HappinessTaskType,
tasker: '',
content: ''
} as HappinessTask
)
}
}
let names = [] as string[];
this.allData.forEach(week => {
if (week.tasks) {
names = names.concat(week.tasks.map(t => t.tasker.trim()));
}
});
this.taskrOptions = names.filter(function (item, pos) {
return !StringUtils.isNullOrWhitespace(item) && names.indexOf(item) == pos;
}).map(name => new DropDownOption(name, name));
}
close() {
this.dlgRef.close();
}
update() {
if (this.processing == false) {
this.processing = true;
this.happinessTaskService.createOrUpdateAll(this.data.tasks).pipe(first()).subscribe(result => {
this.processing = false;
this.dlgRef.close(true);
});
}
}
getTaskTitle(t: HappinessTaskType) {
switch (t) {
case HappinessTaskType.IceBreak: return '帶遊戲';
case HappinessTaskType.Worship: return '唱歌';
case HappinessTaskType.Testimony: return '見證';
case HappinessTaskType.Message: return '信息';
case HappinessTaskType.Gift: return '準備禮物';
case HappinessTaskType.Dessert: return '準備點心';
default: break;
}
}
}

View File

@ -4,29 +4,92 @@
Pastoral Domain
</nb-card-header>
<nb-card-body>
<div class="row" *ngIf="data">
<div class="col-md-6">
<div class="row" *ngIf="!isLoading">
<div class="col-md-4">
<div class="form-group">
<label for="domainType" class="label">Type</label>
<op-drop-down id="domainType" name="domainType" [(ngModel)]='data.type'
[source]="domainTypeOptions"></op-drop-down>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label for="name" class="label">Name</label>
<input type="text" name="name" nbInput fullWidth id="name" [(ngModel)]="data.name">
</div>
</div>
<div class="col-md-6">
<div class="col-md-4">
<div class="form-group">
<label for="leaderMemberId" class="label">Leader</label>
<op-drop-down id="leader" name="leader" [(ngModel)]='data.leaderMemberId'
[source]="leaderOptions"></op-drop-down>
</div>
</div>
<div class="col-md-12">
<div class="form-group">
<label for="description" class="label">Description</label>
<input type="text" name="description" nbInput fullWidth id="description"
[(ngModel)]="data.description">
<textarea type="text" name="description" nbInput fullWidth id="description"
[(ngModel)]="data.description"></textarea>
</div>
</div>
<div class="col-md-12">
<div class="form-group">
<label for="lineGroupId" class="label">Line Group Id</label>
<op-drop-down name="lineGroupId" id="lineGroupId" [(ngModel)]="data.lineGroupId"
[source]="lineClientOptions"></op-drop-down>
</div>
</div>
<div class="col-md-12">
<div class="form-group">
<label for="lineRobot" class="label">Line Robot</label>
<op-drop-down name="lineRobot" id="lineRobot" [(ngModel)]="data.lineAccountId"
[source]="lineRobotOptions"></op-drop-down>
</div>
</div>
<div class="col-md-6">
<div class='form-group'>
<label for='beginTime' class='label'>Service Time</label>
<input type='text' nbInput fullWidth id='beginTime' name='beginTime'
[(ngModel)]='data.serviceTime' [nbDatepicker]="dateTimePicker">
<nb-date-timepicker format="yyyy/MM/dd HH:mm" #dateTimePicker></nb-date-timepicker>
<!-- <input nbInput placeholder="Pick Date" [nbDatepicker]="dateTimePicker">
<nb-date-timepicker #dateTimePicker format="yyyy/MM/dd HH:mm"></nb-date-timepicker>
<nb-datepicker #datepicker></nb-datepicker> -->
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="street" class="label">Address</label>
<input type="text" name="street" nbInput fullWidth id="street"
[(ngModel)]="data.serviceAddress.address">
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="city" class="label">City</label>
<input type="text" name="city" nbInput fullWidth id="city"
[(ngModel)]="data.serviceAddress.city">
</div>
</div>
<div class="col-md-2">
<div class="form-group">
<label for="state" class="label">State</label>
<input type="text" name="state" nbInput fullWidth id="state"
[(ngModel)]="data.serviceAddress.state">
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label for="zip" class="label">Zip</label>
<input type="text" name="zip" nbInput fullWidth id="zip" [(ngModel)]="data.serviceAddress.zip">
</div>
</div>
<!-- <div class="col-md-6">
<div class="form-group">
<label for="image1" class="label">Image1</label>
<input type="text" name="image1" nbInput fullWidth id="image1" [(ngModel)]="data.image1">
@ -55,7 +118,7 @@
<label for="image5" class="label">Image5</label>
<input type="text" name="image5" nbInput fullWidth id="image5" [(ngModel)]="data.image5">
</div>
</div>
</div> -->
</div>

View File

@ -1,9 +1,11 @@
import { Component, OnInit } from '@angular/core';
import { NbDialogRef } from '@nebular/theme';
import { first } from 'rxjs/operators';
import { forkJoin, Observable } from 'rxjs';
import { first, map, mergeMap } from 'rxjs/operators';
import { DropDownOption } from '../../../entity/dropDownOption';
import { FamilyMember } from '../../../entity/Member';
import { PastoralDomain } from '../../../entity/PastoralDomain';
import { AddressInfo, DomainType, PastoralDomain } from '../../../entity/PastoralDomain';
import { LineClientService, LineMessagingAccountService } from '../../../services/crudServices/line-messaging-account.service';
import { PastoralDomainService } from '../../../services/crudServices/pastoral-domain.service';
@Component({
@ -12,21 +14,47 @@ import { PastoralDomainService } from '../../../services/crudServices/pastoral-d
styleUrls: ['./pastoral-domain-editor.component.scss']
})
export class PastoralDomainEditorComponent implements OnInit {
isLoading = true;
isAdding: boolean = false;
processing: boolean = false;
data: PastoralDomain;
members: FamilyMember[];
leaderOptions: DropDownOption[];
leaderOptions: DropDownOption[] = [new DropDownOption(null, 'N/A')];
lineRobotOptions: DropDownOption[] = [new DropDownOption(null, 'N/A')];
lineClientOptions: DropDownOption[] = [new DropDownOption(null, 'N/A')];
domainTypeOptions: DropDownOption[] = [
new DropDownOption(DomainType.CellGroup, 'Cell Group'),
new DropDownOption(DomainType.HappinessGroup, 'Happiness Group'),
];
constructor(
private pastoralDomainService: PastoralDomainService,
private dlgRef: NbDialogRef<PastoralDomainEditorComponent>
private dlgRef: NbDialogRef<PastoralDomainEditorComponent>,
private lineAccountService: LineMessagingAccountService,
private lineClientService: LineClientService
) {
}
ngOnInit(): void {
this.leaderOptions = this.members.map(m => new DropDownOption(m.id, `${m.firstName} ${m.lastName}`));
this.leaderOptions = this.leaderOptions.concat(this.members.map(m => new DropDownOption(m.id, `${m.firstName} ${m.lastName}`)));
if (this.isAdding) {
this.data = { id: '' } as PastoralDomain;
}
if (!this.data.serviceAddress) {
this.data.serviceAddress = new AddressInfo();
}
forkJoin(
[this.lineAccountService.getAll(),
this.lineClientService.getAll()]
// getMultiValueObservable(), forkJoin on works for observables that complete
).pipe(first()).subscribe(result => {
this.lineRobotOptions = this.lineRobotOptions.concat(result[0].map(m => new DropDownOption(m.id, `${m.name} (${500 - m.totalUsage})`)));
this.lineClientOptions = this.lineClientOptions.concat(result[1].filter(l => l.isGroup).map(m => new DropDownOption(m.clientId, `${m.name}`)));
this.isLoading = false;
});
}
close() {

View File

@ -5,13 +5,15 @@ import { Observable } from 'rxjs';
import { first } from 'rxjs/operators';
import { StateService } from '../../@core/utils';
import { FamilyMember } from '../../entity/Member';
import { PastoralDomain } from '../../entity/PastoralDomain';
import { DomainType, PastoralDomain } from '../../entity/PastoralDomain';
import { FamilyMemberService } from '../../services/crudServices/family-member.service';
import { PastoralDomainService } from '../../services/crudServices/pastoral-domain.service';
import { MsgBoxService } from '../../services/msg-box.service';
import { FancySettings } from '../../ui/fancy-table/fancy-settings.model';
import { FancyTableComponent } from '../../ui/fancy-table/fancy-table.component';
import { ObjectUtils } from '../../utilities/object-utils';
import { BestListDlgComponent } from '../happiness-groups/best-list-dlg/best-list-dlg.component';
import { HappinessWeekListDlgComponent } from '../happiness-groups/happiness-week-list-dlg/happiness-week-list-dlg.component';
import { PastoralDomainEditorComponent } from './pastoral-domain-editor/pastoral-domain-editor.component';
@Component({
@ -21,7 +23,6 @@ import { PastoralDomainEditorComponent } from './pastoral-domain-editor/pastoral
})
export class PastoralDomainsComponent implements OnInit {
@ViewChild(FancyTableComponent, { static: false }) fancyTable: FancyTableComponent<PastoralDomain>;
data: PastoralDomain;
allData: PastoralDomain[];
@ -68,10 +69,31 @@ export class PastoralDomainsComponent implements OnInit {
callback: (datum, element) => {
this.delete(datum);
},
}],
},
{
enabled: true,
id: 'happinessBest',
title: 'Bests',
icon: 'smiling-face-outline',
callback: (datum, element) => {
this.openBestListDlg(datum);
},
},
{
enabled: true,
id: 'happinessWeek',
title: 'Week Info',
icon: 'smiling-face-outline',
callback: (datum, element) => {
this.openHappinessWeekListDlg(datum);
},
}
],
onContextMenuOpening: (datum, menuItems) => {
menuItems.find(i => i.id == 'delete').visible = !!datum;
menuItems.find(i => i.id == 'edit').visible = !!datum;
menuItems.find(i => i.id == 'happinessBest').visible = datum.type == DomainType.HappinessGroup;
menuItems.find(i => i.id == 'happinessWeek').visible = datum.type == DomainType.HappinessGroup;
return datum;
},
columns: [
@ -179,5 +201,26 @@ export class PastoralDomainsComponent implements OnInit {
return 'N/A';
}
private openHappinessWeekListDlg(datum: PastoralDomain) {
this.dlgService.open(HappinessWeekListDlgComponent, {
context: {
group: datum
}
}).onClose.pipe(first()).subscribe(result => {
this.getAllData();
});
}
private openBestListDlg(datum: PastoralDomain) {
this.dlgService.open(BestListDlgComponent, {
context: {
group: datum
}
}).onClose.pipe(first()).subscribe(result => {
this.getAllData();
});
}
}

View File

@ -1,30 +1,22 @@
import { PastoralDomain } from "./PastoralDomain";
export interface HappinessWeek {
groupId: string;
happinessGroup: HappinessGroup;
weekId: string;
happinessGroup: PastoralDomain;
id: string;
date: Date;
invitationText: string;
address: string;
cityAndZipCode: string;
seq: number;
updateRestWeekDate: boolean;
}
export interface HappinessGroup {
id: string;
name: string;
beginTime: Date;
address: string;
cityAndZipCode: string;
invitationText: string;
bestList: HappinessBEST[];
weeks: HappinessWeek[];
tasks: HappinessTask[];
topic: string
}
export interface HappinessBEST {
groupId: string;
happinessGroup: HappinessGroup;
happinessGroup: PastoralDomain;
id: string;
name: string;
email: string;

View File

@ -4,4 +4,12 @@ export interface LineMessagingAccount {
chatToken: string;
totalUsage: number;
seq: number;
}
export interface LineClient {
id: string;
clientId: string;
isGroup: boolean;
isManager: boolean;
name: string;
}

View File

@ -1,5 +1,14 @@
import { HappinessBEST, HappinessWeek } from "./HappinessGroup";
import { FamilyMember } from "./Member";
export enum DomainType {
CellGroup,
HappinessGroup,
CellGroupCoworker,
ChurchCoworker,
Person = 99
}
export interface PastoralDomain {
id: string;
name: string;
@ -12,10 +21,18 @@ export interface PastoralDomain {
leaderMemberId: string;
leader: FamilyMember;
familyMembers: FamilyMember[];
lineGroupId: string;
lineAccountId: string;
bests: HappinessBEST[];
happinessWeeks: HappinessWeek[];
serviceAddress: AddressInfo;
type: DomainType;
serviceTime: Date;
}
export class DomainMemberRelationship {
constructor(pastoralDomainId: string, familyMemberId: string) {
this.pastoralDomainId = pastoralDomainId
this.familyMemberId = familyMemberId
@ -23,4 +40,20 @@ export class DomainMemberRelationship {
pastoralDomainId: string;
familyMemberId: string;
}
export class AddressInfo {
/**
*
*/
constructor() {
this.id = "new";
this.address = "";
this.city = "";
this.zip = "";
}
id: string;
address: string;
city: string;
state: string;
zip: string;
}

View File

@ -5,6 +5,7 @@ import { first } from 'rxjs/operators';
import { HappinessBEST, HappinessWeek } from '../entity/HappinessGroup';
import { HappinessGroupService } from '../services/crudServices/happiness-group.service';
import { DateUtils } from '../utilities/date-utils';
import { StringUtils } from '../utilities/string-utils';
declare let TypeIt: any;
@Component({
selector: 'ngx-invitation',
@ -41,7 +42,7 @@ export class InvitationComponent implements OnInit {
this.happinessGroupService.getBest(id).pipe(first()).subscribe(result => {
if (result) {
this.best = result;
this.weeks = this.best.happinessGroup.weeks.sort((a, b) => a.seq - b.seq);
this.weeks = this.best.happinessGroup.happinessWeeks.sort((a, b) => a.seq - b.seq);
if (this.weekNo == -1) {
var today = new Date();
@ -72,7 +73,7 @@ export class InvitationComponent implements OnInit {
if (this.week) {
dummy.value = `${this.week.address} ${this.week.cityAndZipCode}`;
} else {
dummy.value = `${this.best.happinessGroup.address} ${this.best.happinessGroup.cityAndZipCode}`;
dummy.value = `${this.best.happinessGroup.leader} ${StringUtils.AddressCszToString(this.best.happinessGroup.serviceAddress)}`;
}
dummy.select();
document.execCommand("copy");
@ -170,11 +171,11 @@ export class InvitationComponent implements OnInit {
break;
}
if (this.best.happinessGroup.invitationText) {
if (this.best.happinessGroup.description) {
instance
.break({ delay: 500 })
.type(`<em>${this.best.happinessGroup.invitationText}</em>`)
.type(`<em>${this.best.happinessGroup.description}</em>`)
}
instance
@ -212,10 +213,10 @@ export class InvitationComponent implements OnInit {
.break()
.pause(200)
.options({ speed: 50 })
.type(`<span class="typeAddress info hightLight">${week ? week.address : this.best.happinessGroup.address}</span>`)
.type(`<span class="typeAddress info hightLight">${week ? week.address : this.best.happinessGroup.serviceAddress.address}</span>`)
.break()
.pause(200)
.type(`<span class="info">${week ? week.cityAndZipCode : this.best.happinessGroup.cityAndZipCode}</span>`)
.type(`<span class="info">${week ? week.cityAndZipCode : StringUtils.AddressCszToString(this.best.happinessGroup.serviceAddress)}</span>`)
.break()
.options({ speed: 125 })
.exec(async () => {

View File

@ -40,7 +40,7 @@ export class CrudService<T> implements ICrudService<T> {
return this.http.get<T>(this.GET_URL_GET_BY_ID(id));
}
update(data) {
update(data: T) {
return this.http.post<boolean>(this.POST_URL_UPDATE(),
JSON.stringify(data), {
headers: {
@ -50,7 +50,7 @@ export class CrudService<T> implements ICrudService<T> {
});
}
createOrUpdate(data) {
createOrUpdate(data: T) {
return this.http.post<boolean>(this.POST_URL_CREATE_OR_UPDATE(),
JSON.stringify(data), {
headers: {
@ -60,7 +60,7 @@ export class CrudService<T> implements ICrudService<T> {
});
}
createOrUpdateAll(data) {
createOrUpdateAll(data: T[]) {
return this.http.post<boolean>(this.POST_URL_CREATE_OR_UPDATE_ALL(),
JSON.stringify(data), {
headers: {
@ -122,7 +122,7 @@ export class CombinedKeyCrudService<T> implements ICrudService<T> {
return this.http.get<T>(this.GET_URL_GET_BY_ID(ids));
}
update(data) {
update(data: T) {
return this.http.post<boolean>(this.POST_URL_UPDATE(),
JSON.stringify(data), {
headers: {
@ -132,7 +132,7 @@ export class CombinedKeyCrudService<T> implements ICrudService<T> {
});
}
createOrUpdate(data) {
createOrUpdate(data: T) {
return this.http.post<boolean>(this.POST_URL_CREATE_OR_UPDATE(),
JSON.stringify(data), {
headers: {
@ -142,7 +142,7 @@ export class CombinedKeyCrudService<T> implements ICrudService<T> {
});
}
createOrUpdateAll(data) {
createOrUpdateAll(data: T[]) {
return this.http.post<boolean>(this.POST_URL_CREATE_OR_UPDATE_ALL(),
JSON.stringify(data), {
headers: {

View File

@ -2,26 +2,22 @@ import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/internal/Observable';
import { first, map } from 'rxjs/operators';
import { environment } from '../../../environments/environment';
import { HappinessBEST, HappinessGroup } from '../../entity/HappinessGroup';
import { HappinessBEST, HappinessTask } from '../../entity/HappinessGroup';
import { saveAs } from 'file-saver';
import { DateUtils } from '../../utilities/date-utils';
import { CrudService } from './crud.service';
const GET_GROUP_GET_ALL = "/api/HappinessGroup"
const BEST_URL = (id: string = null) => { return `Best${(id ? `/${id}` : '')}` };
const HAPPINESS_GROUP_BASE_URL = (id: string = null) => { return `HappinessGroup${(id ? `/${id}` : '')}` };
const POST_URL_UPDATE_BEST_WEEK = 'HappinessGroup/UpdateBestWeek';
const POST_URL_UPDATE_BEST_WEEK = 'Best/UpdateBestWeek';
@Injectable({
providedIn: 'root'
})
export class HappinessGroupService extends CrudService<HappinessGroup> {
export class HappinessGroupService {
constructor(
protected http: HttpClient,
) {
super(http, HAPPINESS_GROUP_BASE_URL);
//super(http, HAPPINESS_GROUP_BASE_URL);
}
createOrUpdateBestWeek(data) {
return this.http.post<boolean>(POST_URL_UPDATE_BEST_WEEK,
@ -59,3 +55,15 @@ export class HappinessGroupService extends CrudService<HappinessGroup> {
}
const WEEK_TASK_BASE_URL = (action: string = null) => { return `HappinessWeekTask${(action ? `/${action}` : '')}` }
@Injectable({
providedIn: 'root'
})
export class HappinessTaskService extends CrudService<HappinessTask> {
constructor(protected http: HttpClient) {
super(http, WEEK_TASK_BASE_URL);
}
}

View File

@ -3,7 +3,7 @@ import { Injectable } from '@angular/core';
import { NewVisitor } from '../../entity/NewVisitor';
import { CrudService } from './crud.service';
import { first, map } from 'rxjs/operators';
import { LineMessagingAccount } from '../../entity/LineMessagingAccount';
import { LineClient, LineMessagingAccount } from '../../entity/LineMessagingAccount';
const BASE_URL = (action: string = null) => { return `LineMessagingAccount${(action ? `/${action}` : '')}` }
@ -22,3 +22,19 @@ export class LineMessagingAccountService extends CrudService<LineMessagingAccoun
}
}
const LINE_CLIENT_BASE_URL = (action: string = null) => { return `LineClient${(action ? `/${action}` : '')}` }
@Injectable({
providedIn: 'root'
})
export class LineClientService extends CrudService<LineClient> {
constructor(protected http: HttpClient) {
super(http, LINE_CLIENT_BASE_URL);
}
}

View File

@ -1,6 +1,9 @@
//import { FtTagType } from "../components/fancy-table/fancy-row-column.model";
//import { AddressInfo } from "../models/contactInfo.model";
import { stringify } from "querystring";
import { AddressInfo } from "../entity/PastoralDomain";
export class StringUtils {
// Sorting function for SemVer
@ -143,20 +146,34 @@ export class StringUtils {
/**
* Try to parse city sate zip string like `Monrovia, CA 91016` to AddressInfo, if failed will return `null` instead.
*/
// public static tryParseCityStateZip(cityStateZip: string): AddressInfo {
public static tryParseCityStateZip(cityStateZip: string): AddressInfo {
// let addressInfo = new AddressInfo();
// var regex = /([\w\s]*),\s*([A-Z]{2})\s*(\d*-?\d*)/g;
// var match = regex.exec(cityStateZip);
// if (match) {
// addressInfo = new AddressInfo();
// addressInfo.city = match[1];
// addressInfo.state = match[2];
// addressInfo.zip = match[3];
// return addressInfo;
// } else {
// return null;
// }
// }
let addressInfo = new AddressInfo();
var regex = /([\w\s]*),\s*([A-Z]{2})\s*(\d*-?\d*)/g;
var match = regex.exec(cityStateZip);
if (match) {
addressInfo = new AddressInfo();
addressInfo.city = match[1];
addressInfo.state = match[2];
addressInfo.zip = match[3];
return addressInfo;
} else {
return null;
}
}
public static AddressCszToString(info: AddressInfo): string {
let result = '';
let secondPart = '';
if (info) {
if (!this.isNullOrWhitespace(info.city)) result += info.city.trim();
secondPart = `${info.city} ${info.zip}`;
if (!this.isNullOrWhitespace(secondPart)) result += `, ${secondPart.trim()}`
}
return result;
}
}