diff --git a/front-end/src/app/tabs/organizations/organization.page.html b/front-end/src/app/tabs/organizations/organization.page.html new file mode 100644 index 0000000..fb9b3b9 --- /dev/null +++ b/front-end/src/app/tabs/organizations/organization.page.html @@ -0,0 +1,36 @@ + + + {{ 'ORGANIZATIONS.DETAILS' | translate }} + + + + + + + + + + + + + +

{{ 'ORGANIZATIONS.SPEAKERS' | translate }}

+
+
+ + + + + + + {{ 'COMMON.NO_ELEMENT_FOUND' | translate }} + + + + + +
+
+
+
+
diff --git a/front-end/src/app/tabs/organizations/organization.page.scss b/front-end/src/app/tabs/organizations/organization.page.scss new file mode 100644 index 0000000..e69de29 diff --git a/front-end/src/app/tabs/organizations/organization.page.ts b/front-end/src/app/tabs/organizations/organization.page.ts new file mode 100644 index 0000000..8431caf --- /dev/null +++ b/front-end/src/app/tabs/organizations/organization.page.ts @@ -0,0 +1,51 @@ +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; + +import { IDEALoadingService, IDEAMessageService } from '@idea-ionic/common'; + +import { AppService } from 'src/app/app.service'; +import { OrganizationsService } from './organizations.service'; +import { SpeakersService } from '../speakers/speakers.service'; + +import { Speaker } from '@models/speaker.model'; +import { Organization } from '@models/organization.model'; + +@Component({ + selector: 'app-organization', + templateUrl: './organization.page.html', + styleUrls: ['./organization.page.scss'] +}) +export class OrganizationPage implements OnInit { + organization: Organization; + speakers: Speaker[]; + + constructor( + private route: ActivatedRoute, + private loading: IDEALoadingService, + private message: IDEAMessageService, + private _organizations: OrganizationsService, + private _speakers: SpeakersService, + public app: AppService + ) {} + + async ngOnInit() { + await this.loadData(); + } + + async loadData() { + try { + await this.loading.show(); + const organizationId = this.route.snapshot.paramMap.get('organizationId'); + this.organization = await this._organizations.getById(organizationId); + this.speakers = await this._speakers.getList({ organization: this.organization.organizationId, force: true }); + } catch (err) { + this.message.error('COMMON.NOT_FOUND'); + } finally { + await this.loading.hide(); + } + } + + async filterSpeakers(search: string = ''): Promise { + this.speakers = await this._speakers.getList({ search, organization: this.organization.organizationId }); + } +} diff --git a/front-end/src/app/tabs/organizations/organizationCard.component.ts b/front-end/src/app/tabs/organizations/organizationCard.component.ts new file mode 100644 index 0000000..6e1603a --- /dev/null +++ b/front-end/src/app/tabs/organizations/organizationCard.component.ts @@ -0,0 +1,59 @@ +import { Component, Input } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; +import { IonicModule } from '@ionic/angular'; + +import { IDEATranslationsModule } from '@idea-ionic/common'; + +import { AppService } from 'src/app/app.service'; + +import { Organization } from '@models/organization.model'; + +@Component({ + standalone: true, + imports: [CommonModule, FormsModule, IonicModule, IDEATranslationsModule], + selector: 'app-organization-card', + template: ` + + + + {{ organization.name }} + + {{ organization.website }} + + + {{ organization.contactEmail }} + + + +
+ +
+ +
+
+ + + + + + + + + + + + + + + + + + ` +}) +export class OrganizationCardStandaloneComponent { + @Input() organization: Organization; + @Input() preview: boolean; + + constructor(public app: AppService) {} +} diff --git a/front-end/src/app/tabs/organizations/organizations-routing.module.ts b/front-end/src/app/tabs/organizations/organizations-routing.module.ts new file mode 100644 index 0000000..e82c7b6 --- /dev/null +++ b/front-end/src/app/tabs/organizations/organizations-routing.module.ts @@ -0,0 +1,16 @@ +import { NgModule } from '@angular/core'; +import { Routes, RouterModule } from '@angular/router'; + +import { OrganizationsPage } from './organizations.page'; +import { OrganizationPage } from './organization.page'; + +const routes: Routes = [ + { path: '', component: OrganizationsPage }, + { path: ':organizationId', component: OrganizationPage } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class OrganizationsRoutingModule {} diff --git a/front-end/src/app/tabs/organizations/organizations.module.ts b/front-end/src/app/tabs/organizations/organizations.module.ts new file mode 100644 index 0000000..635d747 --- /dev/null +++ b/front-end/src/app/tabs/organizations/organizations.module.ts @@ -0,0 +1,25 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { IonicModule } from '@ionic/angular'; +import { FormsModule } from '@angular/forms'; +import { IDEATranslationsModule } from '@idea-ionic/common'; + +import { OrganizationsRoutingModule } from './organizations-routing.module'; +import { OrganizationPage } from './organization.page'; +import { OrganizationsPage } from './organizations.page'; +import { OrganizationCardStandaloneComponent } from './organizationCard.component'; +import { SpeakerCardStandaloneComponent } from '../speakers/speakerCard.component'; + +@NgModule({ + imports: [ + CommonModule, + FormsModule, + IonicModule, + IDEATranslationsModule, + OrganizationsRoutingModule, + OrganizationCardStandaloneComponent, + SpeakerCardStandaloneComponent + ], + declarations: [OrganizationsPage, OrganizationPage] +}) +export class OrganizationsModule {} diff --git a/front-end/src/app/tabs/organizations/organizations.page.html b/front-end/src/app/tabs/organizations/organizations.page.html new file mode 100644 index 0000000..a23af6b --- /dev/null +++ b/front-end/src/app/tabs/organizations/organizations.page.html @@ -0,0 +1,27 @@ + + + + {{ 'ORGANIZATIONS.LIST' | translate }} + + + + + + + + {{ 'COMMON.NO_ELEMENT_FOUND' | translate }} + + + + + + {{ organization.name }} + + + \ No newline at end of file diff --git a/front-end/src/app/tabs/organizations/organizations.page.scss b/front-end/src/app/tabs/organizations/organizations.page.scss new file mode 100644 index 0000000..e69de29 diff --git a/front-end/src/app/tabs/organizations/organizations.page.ts b/front-end/src/app/tabs/organizations/organizations.page.ts new file mode 100644 index 0000000..e99d4b0 --- /dev/null +++ b/front-end/src/app/tabs/organizations/organizations.page.ts @@ -0,0 +1,49 @@ +import { Component, OnInit, ViewChild } from '@angular/core'; +import { IonContent } from '@ionic/angular'; + +import { Organization } from '@models/organization.model'; +import { OrganizationsService } from './organizations.service'; +import { AppService } from 'src/app/app.service'; +import { IDEALoadingService, IDEAMessageService } from '@idea-ionic/common'; + +@Component({ + selector: 'app-organizations', + templateUrl: './organizations.page.html', + styleUrls: ['./organizations.page.scss'] +}) +export class OrganizationsPage implements OnInit { + @ViewChild(IonContent) content: IonContent; + + organizations: Organization[]; + filteredOrganizations: Organization[]; + + constructor( + private loading: IDEALoadingService, + private message: IDEAMessageService, + private _organizations: OrganizationsService, + public app: AppService + ) {} + + ngOnInit() { + this.loadData(); + } + + async loadData() { + try { + await this.loading.show(); + this.organizations = await this._organizations.getList({}); + } catch (error) { + this.message.error('COMMON.OPERATION_FAILED'); + } finally { + this.loading.hide(); + } + } + + async filterOrganizations(search = ''): Promise { + this.organizations = await this._organizations.getList({ search }); + } + + selectOrganization(organization: Organization) { + this.app.goToInTabs(['organizations', organization.organizationId]); + } +} \ No newline at end of file diff --git a/front-end/src/app/tabs/speakers/speakerCard.component.ts b/front-end/src/app/tabs/speakers/speakerCard.component.ts new file mode 100644 index 0000000..ccda662 --- /dev/null +++ b/front-end/src/app/tabs/speakers/speakerCard.component.ts @@ -0,0 +1,65 @@ +import { Component, Input } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; +import { IonicModule } from '@ionic/angular'; + +import { IDEATranslationsModule } from '@idea-ionic/common'; + +import { AppService } from 'src/app/app.service'; + +import { Speaker } from '@models/speaker.model'; + +@Component({ + standalone: true, + imports: [CommonModule, FormsModule, IonicModule, IDEATranslationsModule], + selector: 'app-speaker-card', + template: ` + + + + {{ speaker.name }} + {{ speaker.description }} + + + + + + + + {{ speaker.name }} + {{ speaker.organization }} + + +
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + ` +}) +export class SpeakerCardStandaloneComponent { + @Input() speaker: Speaker; + @Input() preview: boolean; + + constructor(public app: AppService) {} +} \ No newline at end of file diff --git a/front-end/src/app/tabs/speakers/speakers.service.ts b/front-end/src/app/tabs/speakers/speakers.service.ts index 97f9d59..e638a9e 100644 --- a/front-end/src/app/tabs/speakers/speakers.service.ts +++ b/front-end/src/app/tabs/speakers/speakers.service.ts @@ -14,22 +14,24 @@ export class SpeakersService { constructor(private api: IDEAApiService) {} - private async loadList(): Promise { - this.speakers = (await this.api.getResource(['speakers'])).map(s => new Speaker(s)); + private async loadList(organization?: string): Promise { + this.speakers = (await this.api.getResource(['speakers'], { params: { organization } })).map(s => new Speaker(s)); } /** * Get (and optionally filter) the list of speakers. * Note: it can be paginated. * Note: it's a slice of the array. + * Note: if organization id is passed, it will filter speakers for that organization. */ async getList(options: { force?: boolean; withPagination?: boolean; startPaginationAfterId?: string; search?: string; + organization?: string; }): Promise { - if (!this.speakers || options.force) await this.loadList(); + if (!this.speakers || options.force) await this.loadList(options.organization); if (!this.speakers) return null; options.search = options.search ? String(options.search).toLowerCase() : ''; diff --git a/front-end/src/app/tabs/tabs.routing.module.ts b/front-end/src/app/tabs/tabs.routing.module.ts index 72d5fe5..55df25e 100644 --- a/front-end/src/app/tabs/tabs.routing.module.ts +++ b/front-end/src/app/tabs/tabs.routing.module.ts @@ -24,6 +24,10 @@ const routes: Routes = [ { path: 'rooms', loadChildren: (): Promise => import('./rooms/rooms.module').then(m => m.RoomsModule) + }, + { + path: 'organizations', + loadChildren: (): Promise => import('./organizations/organizations.module').then(m => m.OrganizationsModule) } ] } diff --git a/front-end/src/assets/i18n/en.json b/front-end/src/assets/i18n/en.json index ed45be5..636ed74 100644 --- a/front-end/src/assets/i18n/en.json +++ b/front-end/src/assets/i18n/en.json @@ -337,5 +337,10 @@ "ROOMS": { "DETAILS": "Room details", "SESSIONS": "Sessions hosted in this room" + }, + "ORGANIZATIONS": { + "LIST": "Organizations list", + "DETAILS": "Organization details", + "SPEAKERS": "Speakers from this organization" } }