mirror of
https://gitlab.com/openstapps/openstapps.git
synced 2026-01-21 09:03:02 +00:00
feat: job portal
fix: disable function scoping lint rule fix: outdated contributing docs for adding SCThings
This commit is contained in:
7
.changeset/few-pots-clean.md
Normal file
7
.changeset/few-pots-clean.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
'@openstapps/backend': minor
|
||||||
|
'@openstapps/core': minor
|
||||||
|
'@openstapps/app': minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Add job portal feature
|
||||||
@@ -37,13 +37,13 @@ Adding new types requires changes at multiple locations for it to work correctly
|
|||||||
- Add your SCThing and SCThingWithoutReferences to `src/things/your-thing-name.ts` and make them extend `SCThingWithoutReferences` and `SCThing` respectively
|
- Add your SCThing and SCThingWithoutReferences to `src/things/your-thing-name.ts` and make them extend `SCThingWithoutReferences` and `SCThing` respectively
|
||||||
- Add your SCThingMeta to `src/things/your-thing-name.ts` and make it extend `SCThingMeta`
|
- Add your SCThingMeta to `src/things/your-thing-name.ts` and make it extend `SCThingMeta`
|
||||||
- Add your SCThingMeta to `SCClasses` in `src/meta.ts`
|
- Add your SCThingMeta to `SCClasses` in `src/meta.ts`
|
||||||
- Add your SCThing to `SCThingsWithoutDiff` in `src/meta.ts`
|
- Add your SCThing to `SCIndexableThings ` in `src/meta.ts`
|
||||||
- Add your SCThingWithoutReferences to `SCAssociatedThingWithoutReferences` in `src/meta.ts`
|
- Add your SCThingWithoutReferences to `SCAssociatedThingWithoutReferences` in `src/meta.ts`
|
||||||
- Add your SCThing to `SCAssociatedThing` in `src/meta.ts`
|
- Add your SCThing to `SCAssociatedThing` in `src/meta.ts`
|
||||||
- Add your SCThing to the `SCThingType` enum in `src/things/abstract/thing.ts`
|
- Add your SCThing to the `SCThingType` enum in `src/things/abstract/thing.ts`
|
||||||
- Add an example file for your SCThing in `test/resources/YourThingName.json`
|
- Add an example file for your SCThing in `test/resources/YourThingName.json`
|
||||||
- Add the following lines for your SCThing in `test/type.spec.ts`:
|
- Add the following lines for your SCThing in `test/type.spec.ts`:
|
||||||
|
- Make sure your SCThing (but not SCThingWithoutReferences!) includes the `@indexable` and `@validatable` JSDoc annotations, otherwise neither JSON Schemas nor Elasticsearch mappings will be generated
|
||||||
```typescript
|
```typescript
|
||||||
/**
|
/**
|
||||||
* Types of properties of SCYourThingName
|
* Types of properties of SCYourThingName
|
||||||
|
|||||||
@@ -56,6 +56,19 @@ const menus = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
icon: 'work',
|
||||||
|
route: '/jobs',
|
||||||
|
title: 'job postings',
|
||||||
|
translations: {
|
||||||
|
de: {
|
||||||
|
title: 'Jobangebote',
|
||||||
|
},
|
||||||
|
en: {
|
||||||
|
title: 'job postings',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
title: 'overview',
|
title: 'overview',
|
||||||
route: '/overview',
|
route: '/overview',
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
<item title="search" title.de="Suche" icon="search" route="/search"/>
|
<item title="search" title.de="Suche" icon="search" route="/search"/>
|
||||||
<item title="library catalog" title.de="Bibliothekskatalog" icon="local_library" route="/hebis-search"/>
|
<item title="library catalog" title.de="Bibliothekskatalog" icon="local_library" route="/hebis-search"/>
|
||||||
<item title="course catalog" title.de="Vorlesungsverzeichnis" icon="inventory_2" route="/catalog"/>
|
<item title="course catalog" title.de="Vorlesungsverzeichnis" icon="inventory_2" route="/catalog"/>
|
||||||
|
<item title="job postings" title.de="Jobangebote" icon="work" route="/jobs"/>
|
||||||
</group>
|
</group>
|
||||||
<group title="canteen" title.de="Mensa" icon="local_cafe" route="/canteen"/>
|
<group title="canteen" title.de="Mensa" icon="local_cafe" route="/canteen"/>
|
||||||
<group title="campus map" title.de="Campus Karte" icon="map" route="/map"/>
|
<group title="campus map" title.de="Campus Karte" icon="map" route="/map"/>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
<item title="search" title.de="Suche" icon="search" route="/search"/>
|
<item title="search" title.de="Suche" icon="search" route="/search"/>
|
||||||
<item title="library catalog" title.de="Bibliothekskatalog" icon="local_library" route="/hebis-search"/>
|
<item title="library catalog" title.de="Bibliothekskatalog" icon="local_library" route="/hebis-search"/>
|
||||||
<item title="course catalog" title.de="Vorlesungsverzeichnis" icon="inventory_2" route="/catalog"/>
|
<item title="course catalog" title.de="Vorlesungsverzeichnis" icon="inventory_2" route="/catalog"/>
|
||||||
|
<item title="job postings" title.de="Jobangebote" icon="work" route="/jobs"/>
|
||||||
</group>
|
</group>
|
||||||
<group title="canteen" title.de="Mensa" icon="local_cafe" route="/canteen"/>
|
<group title="canteen" title.de="Mensa" icon="local_cafe" route="/canteen"/>
|
||||||
<group title="campus map" title.de="Campus Karte" icon="map" route="/map"/>
|
<group title="campus map" title.de="Campus Karte" icon="map" route="/map"/>
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ export class MinimalConnector extends Connector<SCMessage> {
|
|||||||
protected async fetchItems(): Promise<SCMessage[]> {
|
protected async fetchItems(): Promise<SCMessage[]> {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
audiences: ['students', 'employees'],
|
audiences: ['students', 'employees', 'guests'],
|
||||||
categories: [],
|
categories: [],
|
||||||
description: 'Some description 1',
|
description: 'Some description 1',
|
||||||
messageBody: 'Some message 1',
|
messageBody: 'Some message 1',
|
||||||
@@ -61,7 +61,7 @@ export class MinimalConnector extends Connector<SCMessage> {
|
|||||||
uid: createUUID({id: 'message_1'}, this.licensePlate),
|
uid: createUUID({id: 'message_1'}, this.licensePlate),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
audiences: ['students', 'employees'],
|
audiences: ['students', 'employees', 'guests'],
|
||||||
categories: [],
|
categories: [],
|
||||||
description: 'Some description 2',
|
description: 'Some description 2',
|
||||||
messageBody: 'Some message 2',
|
messageBody: 'Some message 2',
|
||||||
@@ -71,7 +71,7 @@ export class MinimalConnector extends Connector<SCMessage> {
|
|||||||
uid: '', // see Connetor.getItems()
|
uid: '', // see Connetor.getItems()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
audiences: ['students', 'employees'],
|
audiences: ['students', 'employees', 'guests'],
|
||||||
categories: [],
|
categories: [],
|
||||||
description: 'Some description 3',
|
description: 'Some description 3',
|
||||||
messageBody: 'Some message 3',
|
messageBody: 'Some message 3',
|
||||||
|
|||||||
@@ -46,6 +46,7 @@
|
|||||||
"unicorn/no-nested-ternary": "off",
|
"unicorn/no-nested-ternary": "off",
|
||||||
"unicorn/better-regex": "off",
|
"unicorn/better-regex": "off",
|
||||||
"unicorn/no-non-null-assertion": "off",
|
"unicorn/no-non-null-assertion": "off",
|
||||||
|
"unicorn/consistent-function-scoping": ["error", {"checkArrowFunctions": false}],
|
||||||
"jsdoc/no-types": "error",
|
"jsdoc/no-types": "error",
|
||||||
"jsdoc/require-param": "off",
|
"jsdoc/require-param": "off",
|
||||||
"jsdoc/require-param-description": "error",
|
"jsdoc/require-param-description": "error",
|
||||||
|
|||||||
@@ -129,7 +129,9 @@ describe('dashboard', async function () {
|
|||||||
fixture: 'search/types/message/single-message.json',
|
fixture: 'search/types/message/single-message.json',
|
||||||
}).as('search');
|
}).as('search');
|
||||||
|
|
||||||
cy.get('stapps-news-section').contains('ion-item', 'Mehr Nachrichten').click();
|
cy.get('stapps-news-section')
|
||||||
|
.contains('ion-item', 'Mehr Nachrichten')
|
||||||
|
.click({scrollBehavior: false, force: true});
|
||||||
cy.url().should('include', '/news');
|
cy.url().should('include', '/news');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ const config: IconConfig = {
|
|||||||
'settings',
|
'settings',
|
||||||
'info',
|
'info',
|
||||||
'rate_review',
|
'rate_review',
|
||||||
|
'work',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
codePoints: {
|
codePoints: {
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ import {UtilModule} from './util/util.module';
|
|||||||
import {initLogger} from './_helpers/ts-logger';
|
import {initLogger} from './_helpers/ts-logger';
|
||||||
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
||||||
import {AboutModule} from './modules/about/about.module';
|
import {AboutModule} from './modules/about/about.module';
|
||||||
|
import {JobModule} from './modules/jobs/jobs.module';
|
||||||
import {FavoritesModule} from './modules/favorites/favorites.module';
|
import {FavoritesModule} from './modules/favorites/favorites.module';
|
||||||
import {ProfilePageModule} from './modules/profile/profile.module';
|
import {ProfilePageModule} from './modules/profile/profile.module';
|
||||||
import {FeedbackModule} from './modules/feedback/feedback.module';
|
import {FeedbackModule} from './modules/feedback/feedback.module';
|
||||||
@@ -147,6 +148,7 @@ export function createTranslateLoader(http: HttpClient) {
|
|||||||
HebisModule,
|
HebisModule,
|
||||||
IonicModule.forRoot(),
|
IonicModule.forRoot(),
|
||||||
IonIconModule,
|
IonIconModule,
|
||||||
|
JobModule,
|
||||||
FavoritesModule,
|
FavoritesModule,
|
||||||
LibraryModule,
|
LibraryModule,
|
||||||
HttpClientModule,
|
HttpClientModule,
|
||||||
|
|||||||
@@ -48,11 +48,12 @@ export class AuthHelperService {
|
|||||||
private browser: SimpleBrowser,
|
private browser: SimpleBrowser,
|
||||||
private alertController: AlertController,
|
private alertController: AlertController,
|
||||||
) {
|
) {
|
||||||
this.userConfigurationMap = (
|
this.userConfigurationMap =
|
||||||
this.configProvider.getAnyValue('auth') as {
|
(
|
||||||
default: SCAuthorizationProvider;
|
this.configProvider.getAnyValue('auth') as {
|
||||||
}
|
default: SCAuthorizationProvider;
|
||||||
).default.endpoints.mapping;
|
}
|
||||||
|
).default?.endpoints.mapping ?? {};
|
||||||
}
|
}
|
||||||
|
|
||||||
public getAuthMessage(provider: SCAuthorizationProviderType, action: IAuthAction | IPAIAAuthAction) {
|
public getAuthMessage(provider: SCAuthorizationProviderType, action: IAuthAction | IPAIAAuthAction) {
|
||||||
|
|||||||
@@ -48,4 +48,5 @@
|
|||||||
<stapps-news-section></stapps-news-section>
|
<stapps-news-section></stapps-news-section>
|
||||||
<stapps-mensa-section></stapps-mensa-section>
|
<stapps-mensa-section></stapps-mensa-section>
|
||||||
<stapps-favorites-section></stapps-favorites-section>
|
<stapps-favorites-section></stapps-favorites-section>
|
||||||
|
<stapps-job-section></stapps-job-section>
|
||||||
</ion-content>
|
</ion-content>
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ import {ThingTranslateModule} from '../../translation/thing-translate.module';
|
|||||||
import {UtilModule} from '../../util/util.module';
|
import {UtilModule} from '../../util/util.module';
|
||||||
import {IonIconModule} from '../../util/ion-icon/ion-icon.module';
|
import {IonIconModule} from '../../util/ion-icon/ion-icon.module';
|
||||||
import {NewsModule} from '../news/news.module';
|
import {NewsModule} from '../news/news.module';
|
||||||
|
import {JobSectionComponent} from './sections/jobs-section/job-section.component';
|
||||||
|
import {JobModule} from '../jobs/jobs.module';
|
||||||
|
|
||||||
const catalogRoutes: Routes = [
|
const catalogRoutes: Routes = [
|
||||||
{
|
{
|
||||||
@@ -51,6 +53,7 @@ const catalogRoutes: Routes = [
|
|||||||
MensaSectionContentComponent,
|
MensaSectionContentComponent,
|
||||||
FavoritesSectionComponent,
|
FavoritesSectionComponent,
|
||||||
DashboardComponent,
|
DashboardComponent,
|
||||||
|
JobSectionComponent,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
IonicModule.forRoot(),
|
IonicModule.forRoot(),
|
||||||
@@ -65,6 +68,7 @@ const catalogRoutes: Routes = [
|
|||||||
ThingTranslateModule.forChild(),
|
ThingTranslateModule.forChild(),
|
||||||
UtilModule,
|
UtilModule,
|
||||||
NewsModule,
|
NewsModule,
|
||||||
|
JobModule,
|
||||||
],
|
],
|
||||||
providers: [SettingsProvider, TranslatePipe],
|
providers: [SettingsProvider, TranslatePipe],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
<!--
|
||||||
|
~ Copyright (C) 2023 StApps
|
||||||
|
~ This program is free software: you can redistribute it and/or modify it
|
||||||
|
~ under the terms of the GNU General Public License as published by the Free
|
||||||
|
~ Software Foundation, version 3.
|
||||||
|
~
|
||||||
|
~ This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
~ more details.
|
||||||
|
~
|
||||||
|
~ You should have received a copy of the GNU General Public License along with
|
||||||
|
~ this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<stapps-section [title]="'dashboard.jobs.title' | translate">
|
||||||
|
<ion-button slot="button-end" fill="clear" color="medium" [routerLink]="['/jobs']">
|
||||||
|
<ion-icon slot="icon-only" name="search" size="24"></ion-icon>
|
||||||
|
</ion-button>
|
||||||
|
<simple-swiper *ngIf="jobs | async as jobs; else noItems" @fade>
|
||||||
|
<stapps-data-list-item
|
||||||
|
*ngFor="let item of jobs"
|
||||||
|
[hideThumbnail]="true"
|
||||||
|
[item]="item"
|
||||||
|
appearance="square"
|
||||||
|
></stapps-data-list-item>
|
||||||
|
<ion-item [routerLink]="['/jobs']" class="more-jobs" lines="none">
|
||||||
|
<ion-label>{{ 'dashboard.jobs.title' | translate | titlecase }}</ion-label>
|
||||||
|
<ion-icon color="medium" name="read_more" size="40"></ion-icon>
|
||||||
|
</ion-item>
|
||||||
|
</simple-swiper>
|
||||||
|
<ng-template #noItems>
|
||||||
|
<ion-item class="nothing-selected" lines="none">
|
||||||
|
<ion-label class="ion-text-wrap">
|
||||||
|
{{ 'dashboard.jobs.noJobs' | translate }}
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
|
||||||
|
<ion-button slot="button-end" fill="clear" color="medium" [routerLink]="['/jobs']">
|
||||||
|
<ion-icon slot="icon-only" name="search" size="24"></ion-icon>
|
||||||
|
</ion-button>
|
||||||
|
</ng-template>
|
||||||
|
</stapps-section>
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/*!
|
||||||
|
* Copyright (C) 2023 StApps
|
||||||
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
* Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with
|
||||||
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.nothing-selected::part(native) {
|
||||||
|
color: var(--ion-color-medium-shade);
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
simple-swiper {
|
||||||
|
--swiper-slide-width: 280px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.more-jobs {
|
||||||
|
--color: var(--ion-color-medium-tint);
|
||||||
|
|
||||||
|
font-size: var(--font-size-xl);
|
||||||
|
|
||||||
|
&::part(native) {
|
||||||
|
height: 100%;
|
||||||
|
background: none;
|
||||||
|
border-radius: var(--border-radius-default);
|
||||||
|
}
|
||||||
|
|
||||||
|
ion-label {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ion-icon {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 100;
|
||||||
|
right: 10px;
|
||||||
|
bottom: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 StApps
|
||||||
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
* Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with
|
||||||
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
import {ChangeDetectionStrategy, Component, inject} from '@angular/core';
|
||||||
|
import {SCSearchResult, SCThingType} from '@openstapps/core';
|
||||||
|
import {DataProvider} from 'src/app/modules/data/data.provider';
|
||||||
|
import {fadeAnimation} from '../../fade.animation';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows a section with meals of the chosen mensa
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'stapps-job-section',
|
||||||
|
templateUrl: 'job-section.component.html',
|
||||||
|
styleUrls: ['job-section.component.scss'],
|
||||||
|
animations: [fadeAnimation],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
})
|
||||||
|
export class JobSectionComponent {
|
||||||
|
jobs = inject(DataProvider)
|
||||||
|
.search({
|
||||||
|
filter: {type: 'value', arguments: {field: 'type', value: SCThingType.JobPosting}},
|
||||||
|
size: 5,
|
||||||
|
from: 0,
|
||||||
|
})
|
||||||
|
.then((result: SCSearchResult) => result.data);
|
||||||
|
}
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
import {Component, Input} from '@angular/core';
|
import {Component, Input} from '@angular/core';
|
||||||
import {SCDateSeries, SCThings, SCThingType} from '@openstapps/core';
|
import {SCDateSeries, SCThingType, SCThings} from '@openstapps/core';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows a horizontal list of action chips
|
* Shows a horizontal list of action chips
|
||||||
@@ -37,12 +37,20 @@ export class ActionChipListComponent {
|
|||||||
@Input() set item(item: SCThings) {
|
@Input() set item(item: SCThings) {
|
||||||
this._item = item;
|
this._item = item;
|
||||||
|
|
||||||
|
const isInPlace = 'inPlace' in item && !!item.inPlace && 'geo' in item.inPlace;
|
||||||
|
const hasDirectGeo = 'geo' in item;
|
||||||
|
const maybeCoords = isInPlace
|
||||||
|
? item?.inPlace?.geo.point.coordinates
|
||||||
|
: hasDirectGeo
|
||||||
|
? item.geo.point.coordinates
|
||||||
|
: undefined;
|
||||||
|
const isNullIsland = maybeCoords ? maybeCoords[0] === 0 && maybeCoords[1] === 0 : false;
|
||||||
this.applicable = {
|
this.applicable = {
|
||||||
locate: false, // TODO: reimplement this at a later date
|
locate: false, // TODO: reimplement this at a later date
|
||||||
event:
|
event:
|
||||||
item.type === SCThingType.AcademicEvent ||
|
item.type === SCThingType.AcademicEvent ||
|
||||||
(item.type === SCThingType.DateSeries && (item as SCDateSeries).dates.length > 0),
|
(item.type === SCThingType.DateSeries && (item as SCDateSeries).dates.length > 0),
|
||||||
navigate: ('inPlace' in item && item.inPlace && 'geo' in item.inPlace) || 'geo' in item,
|
navigate: (hasDirectGeo || isInPlace) && !isNullIsland,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,4 +46,5 @@ export const DataIcons: Record<SCThingType, string> = {
|
|||||||
'tour': SCIcon`tour`,
|
'tour': SCIcon`tour`,
|
||||||
'video': SCIcon`movie`,
|
'video': SCIcon`movie`,
|
||||||
'diff': SCIcon`difference`,
|
'diff': SCIcon`difference`,
|
||||||
|
'job posting': SCIcon`work`,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -17,93 +17,95 @@ import {CommonModule} from '@angular/common';
|
|||||||
import {HttpClientModule} from '@angular/common/http';
|
import {HttpClientModule} from '@angular/common/http';
|
||||||
import {NgModule} from '@angular/core';
|
import {NgModule} from '@angular/core';
|
||||||
import {FormsModule} from '@angular/forms';
|
import {FormsModule} from '@angular/forms';
|
||||||
|
import {LeafletModule} from '@asymmetrik/ngx-leaflet';
|
||||||
import {IonicModule, Platform} from '@ionic/angular';
|
import {IonicModule, Platform} from '@ionic/angular';
|
||||||
import {TranslateModule} from '@ngx-translate/core';
|
import {TranslateModule} from '@ngx-translate/core';
|
||||||
import {MarkdownModule} from 'ngx-markdown';
|
import {MarkdownModule} from 'ngx-markdown';
|
||||||
import {MomentModule} from 'ngx-moment';
|
import {MomentModule} from 'ngx-moment';
|
||||||
import {ThingTranslateModule} from '../../translation/thing-translate.module';
|
import {ThingTranslateModule} from '../../translation/thing-translate.module';
|
||||||
import {MenuModule} from '../menu/menu.module';
|
import {SimpleBrowser, browserFactory} from '../../util/browser.factory';
|
||||||
|
import {IonIconModule} from '../../util/ion-icon/ion-icon.module';
|
||||||
|
import {RoutingStackService} from '../../util/routing-stack.service';
|
||||||
|
import {UtilModule} from '../../util/util.module';
|
||||||
|
import {CalendarService} from '../calendar/calendar.service';
|
||||||
import {ScheduleProvider} from '../calendar/schedule.provider';
|
import {ScheduleProvider} from '../calendar/schedule.provider';
|
||||||
|
import {GeoNavigationDirective} from '../map/geo-navigation.directive';
|
||||||
|
import {MapWidgetComponent} from '../map/widget/map-widget.component';
|
||||||
|
import {MenuModule} from '../menu/menu.module';
|
||||||
|
import {SettingsProvider} from '../settings/settings.provider';
|
||||||
import {StorageModule} from '../storage/storage.module';
|
import {StorageModule} from '../storage/storage.module';
|
||||||
import {ActionChipListComponent} from './chips/action-chip-list.component';
|
import {ActionChipListComponent} from './chips/action-chip-list.component';
|
||||||
import {EditEventSelectionComponent} from './chips/edit-event-selection.component';
|
|
||||||
import {AddEventActionChipComponent} from './chips/data/add-event-action-chip.component';
|
import {AddEventActionChipComponent} from './chips/data/add-event-action-chip.component';
|
||||||
import {LocateActionChipComponent} from './chips/data/locate-action-chip.component';
|
import {LocateActionChipComponent} from './chips/data/locate-action-chip.component';
|
||||||
|
import {NavigateActionChipComponent} from './chips/data/navigate-action-chip.component';
|
||||||
|
import {EditEventSelectionComponent} from './chips/edit-event-selection.component';
|
||||||
|
import {CoordinatedSearchProvider} from './coordinated-search.provider';
|
||||||
import {DataFacetsProvider} from './data-facets.provider';
|
import {DataFacetsProvider} from './data-facets.provider';
|
||||||
import {DataIconPipe} from './data-icon.pipe';
|
import {DataIconPipe} from './data-icon.pipe';
|
||||||
import {DataRoutingModule} from './data-routing.module';
|
import {DataRoutingModule} from './data-routing.module';
|
||||||
import {DataProvider} from './data.provider';
|
import {DataProvider} from './data.provider';
|
||||||
import {DataDetailContentComponent} from './detail/data-detail-content.component';
|
import {DataDetailContentComponent} from './detail/data-detail-content.component';
|
||||||
import {DataDetailComponent} from './detail/data-detail.component';
|
import {DataDetailComponent} from './detail/data-detail.component';
|
||||||
|
import {DataPathComponent} from './detail/data-path.component';
|
||||||
import {AddressDetailComponent} from './elements/address-detail.component';
|
import {AddressDetailComponent} from './elements/address-detail.component';
|
||||||
|
import {CertificationsInDetailComponent} from './elements/certifications-in-detail.component';
|
||||||
|
import {ExternalLinkComponent} from './elements/external-link.component';
|
||||||
|
import {FavoriteButtonComponent} from './elements/favorite-button.component';
|
||||||
|
import {LongInlineTextComponent} from './elements/long-inline-text.component';
|
||||||
import {OffersDetailComponent} from './elements/offers-detail.component';
|
import {OffersDetailComponent} from './elements/offers-detail.component';
|
||||||
import {OffersInListComponent} from './elements/offers-in-list.component';
|
import {OffersInListComponent} from './elements/offers-in-list.component';
|
||||||
import {OriginDetailComponent} from './elements/origin-detail.component';
|
import {OriginDetailComponent} from './elements/origin-detail.component';
|
||||||
|
import {OriginInListComponent} from './elements/origin-in-list.component';
|
||||||
|
import {StappsRatingComponent} from './elements/rating.component';
|
||||||
import {SimpleCardComponent} from './elements/simple-card.component';
|
import {SimpleCardComponent} from './elements/simple-card.component';
|
||||||
|
import {SkeletonListItemComponent} from './elements/skeleton-list-item.component';
|
||||||
|
import {SkeletonSegmentComponent} from './elements/skeleton-segment-button.component';
|
||||||
|
import {SkeletonSimpleCardComponent} from './elements/skeleton-simple-card.component';
|
||||||
|
import {TitleCardComponent} from './elements/title-card.component';
|
||||||
|
import {DataListItemHostDefaultComponent} from './list/data-list-item-host-default.component';
|
||||||
|
import {DataListItemHostDirective} from './list/data-list-item-host.directive';
|
||||||
|
import {DataListItemComponent} from './list/data-list-item.component';
|
||||||
import {DataListComponent} from './list/data-list.component';
|
import {DataListComponent} from './list/data-list.component';
|
||||||
import {FoodDataListComponent} from './list/food-data-list.component';
|
import {FoodDataListComponent} from './list/food-data-list.component';
|
||||||
import {SearchPageComponent} from './list/search-page.component';
|
import {SearchPageComponent} from './list/search-page.component';
|
||||||
|
import {SimpleDataListComponent} from './list/simple-data-list.component';
|
||||||
|
import {SkeletonListComponent} from './list/skeleton-list.component';
|
||||||
|
import {TreeListFragmentComponent} from './list/tree-list-fragment.component';
|
||||||
|
import {TreeListComponent} from './list/tree-list.component';
|
||||||
import {StAppsWebHttpClient} from './stapps-web-http-client.provider';
|
import {StAppsWebHttpClient} from './stapps-web-http-client.provider';
|
||||||
|
import {ArticleContentComponent} from './types/article/article-content.component';
|
||||||
|
import {ArticleListItemComponent} from './types/article/article-item.component';
|
||||||
|
import {BookDetailContentComponent} from './types/book/book-detail-content.component';
|
||||||
|
import {BookListItemComponent} from './types/book/book-list-item.component';
|
||||||
import {CatalogDetailContentComponent} from './types/catalog/catalog-detail-content.component';
|
import {CatalogDetailContentComponent} from './types/catalog/catalog-detail-content.component';
|
||||||
|
import {CatalogListItemComponent} from './types/catalog/catalog-list-item.component';
|
||||||
import {DateSeriesDetailContentComponent} from './types/date-series/date-series-detail-content.component';
|
import {DateSeriesDetailContentComponent} from './types/date-series/date-series-detail-content.component';
|
||||||
|
import {DateSeriesListItemComponent} from './types/date-series/date-series-list-item.component';
|
||||||
|
import {DishCharacteristicsComponent} from './types/dish/dish-characteristics.component';
|
||||||
import {DishDetailContentComponent} from './types/dish/dish-detail-content.component';
|
import {DishDetailContentComponent} from './types/dish/dish-detail-content.component';
|
||||||
|
import {DishListItemComponent} from './types/dish/dish-list-item.component';
|
||||||
import {EventDetailContentComponent} from './types/event/event-detail-content.component';
|
import {EventDetailContentComponent} from './types/event/event-detail-content.component';
|
||||||
import {EventListItemComponent} from './types/event/event-list-item.component';
|
import {EventListItemComponent} from './types/event/event-list-item.component';
|
||||||
|
import {EventRoutePathComponent} from './types/event/event-route-path.component';
|
||||||
import {FavoriteDetailContentComponent} from './types/favorite/favorite-detail-content.component';
|
import {FavoriteDetailContentComponent} from './types/favorite/favorite-detail-content.component';
|
||||||
|
import {FavoriteListItemComponent} from './types/favorite/favorite-list-item.component';
|
||||||
|
import {JobPostingDetailContentComponent} from './types/job-posting/job-posting-detail-content.component';
|
||||||
|
import {JobPostingListItemComponent} from './types/job-posting/job-posting-list-item.component';
|
||||||
import {MessageDetailContentComponent} from './types/message/message-detail-content.component';
|
import {MessageDetailContentComponent} from './types/message/message-detail-content.component';
|
||||||
|
import {MessageListItemComponent} from './types/message/message-list-item.component';
|
||||||
import {OrganizationDetailContentComponent} from './types/organization/organization-detail-content.component';
|
import {OrganizationDetailContentComponent} from './types/organization/organization-detail-content.component';
|
||||||
|
import {OrganizationListItemComponent} from './types/organization/organization-list-item.component';
|
||||||
|
import {PeriodicalDetailContentComponent} from './types/periodical/periodical-detail-content.component';
|
||||||
|
import {PeriodicalListItemComponent} from './types/periodical/periodical-list-item.component';
|
||||||
import {PersonDetailContentComponent} from './types/person/person-detail-content.component';
|
import {PersonDetailContentComponent} from './types/person/person-detail-content.component';
|
||||||
|
import {PersonListItemComponent} from './types/person/person-list-item.component';
|
||||||
import {PlaceDetailContentComponent} from './types/place/place-detail-content.component';
|
import {PlaceDetailContentComponent} from './types/place/place-detail-content.component';
|
||||||
import {PlaceListItemComponent} from './types/place/place-list-item.component';
|
import {PlaceListItemComponent} from './types/place/place-list-item.component';
|
||||||
import {PlaceMensaDetailComponent} from './types/place/special/mensa/place-mensa-detail.component';
|
import {PlaceMensaDetailComponent} from './types/place/special/mensa/place-mensa-detail.component';
|
||||||
import {SemesterDetailContentComponent} from './types/semester/semester-detail-content.component';
|
import {SemesterDetailContentComponent} from './types/semester/semester-detail-content.component';
|
||||||
import {MapWidgetComponent} from '../map/widget/map-widget.component';
|
|
||||||
import {LeafletModule} from '@asymmetrik/ngx-leaflet';
|
|
||||||
import {SkeletonSimpleCardComponent} from './elements/skeleton-simple-card.component';
|
|
||||||
import {CatalogListItemComponent} from './types/catalog/catalog-list-item.component';
|
|
||||||
import {DataListItemComponent} from './list/data-list-item.component';
|
|
||||||
import {DateSeriesListItemComponent} from './types/date-series/date-series-list-item.component';
|
|
||||||
import {DishListItemComponent} from './types/dish/dish-list-item.component';
|
|
||||||
import {FavoriteListItemComponent} from './types/favorite/favorite-list-item.component';
|
|
||||||
import {LongInlineTextComponent} from './elements/long-inline-text.component';
|
|
||||||
import {MessageListItemComponent} from './types/message/message-list-item.component';
|
|
||||||
import {OrganizationListItemComponent} from './types/organization/organization-list-item.component';
|
|
||||||
import {PersonListItemComponent} from './types/person/person-list-item.component';
|
|
||||||
import {SkeletonListItemComponent} from './elements/skeleton-list-item.component';
|
|
||||||
import {SkeletonSegmentComponent} from './elements/skeleton-segment-button.component';
|
|
||||||
import {VideoDetailContentComponent} from './types/video/video-detail-content.component';
|
|
||||||
import {SemesterListItemComponent} from './types/semester/semester-list-item.component';
|
import {SemesterListItemComponent} from './types/semester/semester-list-item.component';
|
||||||
|
import {VideoDetailContentComponent} from './types/video/video-detail-content.component';
|
||||||
import {VideoListItemComponent} from './types/video/video-list-item.component';
|
import {VideoListItemComponent} from './types/video/video-list-item.component';
|
||||||
import {OriginInListComponent} from './elements/origin-in-list.component';
|
|
||||||
import {CoordinatedSearchProvider} from './coordinated-search.provider';
|
|
||||||
import {FavoriteButtonComponent} from './elements/favorite-button.component';
|
|
||||||
import {SimpleDataListComponent} from './list/simple-data-list.component';
|
|
||||||
import {TitleCardComponent} from './elements/title-card.component';
|
|
||||||
import {CalendarService} from '../calendar/calendar.service';
|
|
||||||
import {RoutingStackService} from '../../util/routing-stack.service';
|
|
||||||
import {DataPathComponent} from './detail/data-path.component';
|
|
||||||
import {EventRoutePathComponent} from './types/event/event-route-path.component';
|
|
||||||
import {UtilModule} from '../../util/util.module';
|
|
||||||
import {TreeListComponent} from './list/tree-list.component';
|
|
||||||
import {TreeListFragmentComponent} from './list/tree-list-fragment.component';
|
|
||||||
import {SettingsProvider} from '../settings/settings.provider';
|
|
||||||
import {IonIconModule} from '../../util/ion-icon/ion-icon.module';
|
|
||||||
import {ExternalLinkComponent} from './elements/external-link.component';
|
|
||||||
import {ArticleListItemComponent} from './types/article/article-item.component';
|
|
||||||
import {ArticleContentComponent} from './types/article/article-content.component';
|
|
||||||
import {BookDetailContentComponent} from './types/book/book-detail-content.component';
|
|
||||||
import {BookListItemComponent} from './types/book/book-list-item.component';
|
|
||||||
import {PeriodicalListItemComponent} from './types/periodical/periodical-list-item.component';
|
|
||||||
import {PeriodicalDetailContentComponent} from './types/periodical/periodical-detail-content.component';
|
|
||||||
import {DataListItemHostDirective} from './list/data-list-item-host.directive';
|
|
||||||
import {DataListItemHostDefaultComponent} from './list/data-list-item-host-default.component';
|
|
||||||
import {browserFactory, SimpleBrowser} from '../../util/browser.factory';
|
|
||||||
import {StappsRatingComponent} from './elements/rating.component';
|
|
||||||
import {DishCharacteristicsComponent} from './types/dish/dish-characteristics.component';
|
|
||||||
import {SkeletonListComponent} from './list/skeleton-list.component';
|
|
||||||
import {CertificationsInDetailComponent} from './elements/certifications-in-detail.component';
|
|
||||||
import {GeoNavigationDirective} from '../map/geo-navigation.directive';
|
|
||||||
import {NavigateActionChipComponent} from './chips/data/navigate-action-chip.component';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Module for handling data
|
* Module for handling data
|
||||||
@@ -142,6 +144,8 @@ import {NavigateActionChipComponent} from './chips/data/navigate-action-chip.com
|
|||||||
MapWidgetComponent,
|
MapWidgetComponent,
|
||||||
MessageDetailContentComponent,
|
MessageDetailContentComponent,
|
||||||
MessageListItemComponent,
|
MessageListItemComponent,
|
||||||
|
JobPostingDetailContentComponent,
|
||||||
|
JobPostingListItemComponent,
|
||||||
OffersDetailComponent,
|
OffersDetailComponent,
|
||||||
OffersInListComponent,
|
OffersInListComponent,
|
||||||
OrganizationDetailContentComponent,
|
OrganizationDetailContentComponent,
|
||||||
|
|||||||
@@ -58,6 +58,10 @@
|
|||||||
[item]="$any(item)"
|
[item]="$any(item)"
|
||||||
*ngSwitchCase="'message'"
|
*ngSwitchCase="'message'"
|
||||||
></stapps-message-detail-content>
|
></stapps-message-detail-content>
|
||||||
|
<stapps-job-posting-detail-content
|
||||||
|
[item]="$any(item)"
|
||||||
|
*ngSwitchCase="'job posting'"
|
||||||
|
></stapps-job-posting-detail-content>
|
||||||
<stapps-person-detail-content [item]="$any(item)" *ngSwitchCase="'person'"></stapps-person-detail-content>
|
<stapps-person-detail-content [item]="$any(item)" *ngSwitchCase="'person'"></stapps-person-detail-content>
|
||||||
<stapps-place-detail-content [item]="$any(item)" *ngSwitchCase="'building'"></stapps-place-detail-content>
|
<stapps-place-detail-content [item]="$any(item)" *ngSwitchCase="'building'"></stapps-place-detail-content>
|
||||||
<stapps-place-detail-content [item]="$any(item)" *ngSwitchCase="'floor'"></stapps-place-detail-content>
|
<stapps-place-detail-content [item]="$any(item)" *ngSwitchCase="'floor'"></stapps-place-detail-content>
|
||||||
|
|||||||
@@ -30,15 +30,17 @@
|
|||||||
<div *ngIf="$any(item).openingHours" class="opening-hours">
|
<div *ngIf="$any(item).openingHours" class="opening-hours">
|
||||||
<stapps-opening-hours [openingHours]="item.openingHours"></stapps-opening-hours>
|
<stapps-opening-hours [openingHours]="item.openingHours"></stapps-opening-hours>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="item.description" class="description">
|
<!-- TODO obviously this is bad style. Tbd where to put the differentiation. Job Postings always have a description, but it's going to be shown in `stapps-job-posting-detail-content` anyways, no need to repeat here. For this view, I would use other fields of the schema.org JobPosting like the `ThingWithCategory.category` -->
|
||||||
|
<div *ngIf="item.description && item.type !== 'job posting'" class="description">
|
||||||
<div class="text-accordion" [style.-webkit-line-clamp]="descriptionLinesToDisplay" #accordionTextArea>
|
<div class="text-accordion" [style.-webkit-line-clamp]="descriptionLinesToDisplay" #accordionTextArea>
|
||||||
{{ 'description' | thingTranslate : item }}
|
{{ 'description' | thingTranslate : item }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- TODO see above -->
|
||||||
<ion-button
|
<ion-button
|
||||||
expand="full"
|
expand="full"
|
||||||
fill="clear"
|
fill="clear"
|
||||||
*ngIf="item.description && buttonShown"
|
*ngIf="item.description && item.type !== 'job posting' && buttonShown"
|
||||||
(click)="toggleDescriptionAccordion()"
|
(click)="toggleDescriptionAccordion()"
|
||||||
>
|
>
|
||||||
<ion-icon [name]="buttonState" size="large"></ion-icon>
|
<ion-icon [name]="buttonState" size="large"></ion-icon>
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import {PeriodicalListItemComponent} from '../types/periodical/periodical-list-i
|
|||||||
import {DataListItemHostDefaultComponent} from './data-list-item-host-default.component';
|
import {DataListItemHostDefaultComponent} from './data-list-item-host-default.component';
|
||||||
import {ArticleListItemComponent} from '../types/article/article-item.component';
|
import {ArticleListItemComponent} from '../types/article/article-item.component';
|
||||||
import {DishListItemComponent} from '../types/dish/dish-list-item.component';
|
import {DishListItemComponent} from '../types/dish/dish-list-item.component';
|
||||||
|
import {JobPostingListItemComponent} from '../types/job-posting/job-posting-list-item.component';
|
||||||
|
|
||||||
export interface DataListItem {
|
export interface DataListItem {
|
||||||
item: SCThings;
|
item: SCThings;
|
||||||
@@ -53,6 +54,7 @@ const DataListItemIndex: Partial<Record<SCThingType, Type<DataListItem>>> = {
|
|||||||
[SCThingType.Periodical]: PeriodicalListItemComponent,
|
[SCThingType.Periodical]: PeriodicalListItemComponent,
|
||||||
[SCThingType.Book]: BookListItemComponent,
|
[SCThingType.Book]: BookListItemComponent,
|
||||||
[SCThingType.Article]: ArticleListItemComponent,
|
[SCThingType.Article]: ArticleListItemComponent,
|
||||||
|
[SCThingType.JobPosting]: JobPostingListItemComponent,
|
||||||
};
|
};
|
||||||
|
|
||||||
@Directive({
|
@Directive({
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019 StApps
|
||||||
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
* Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with
|
||||||
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
import {Component, Input} from '@angular/core';
|
||||||
|
import {SCJobPosting} from '@openstapps/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'stapps-job-posting-detail-content',
|
||||||
|
templateUrl: 'job-posting-detail-content.html',
|
||||||
|
styleUrls: ['job-posting-detail-content.scss'],
|
||||||
|
})
|
||||||
|
export class JobPostingDetailContentComponent {
|
||||||
|
@Input() item: SCJobPosting;
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
<!--
|
||||||
|
~ Copyright (C) 2023 StApps
|
||||||
|
~ This program is free software: you can redistribute it and/or modify it
|
||||||
|
~ under the terms of the GNU General Public License as published by the Free
|
||||||
|
~ Software Foundation, version 3.
|
||||||
|
~
|
||||||
|
~ This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
~ more details.
|
||||||
|
~
|
||||||
|
~ You should have received a copy of the GNU General Public License along with
|
||||||
|
~ this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<ion-card>
|
||||||
|
<ion-card-content [innerHtml]="item.description"> </ion-card-content>
|
||||||
|
</ion-card>
|
||||||
|
<ion-card *ngIf="item.sameAs">
|
||||||
|
<ion-card-header> {{ 'sameAs' | propertyNameTranslate : item | titlecase }} </ion-card-header>
|
||||||
|
<ion-card-content>
|
||||||
|
<stapps-external-link [link]="item.sameAs" [text]="item.name"></stapps-external-link>
|
||||||
|
</ion-card-content>
|
||||||
|
</ion-card>
|
||||||
|
|
||||||
|
<ion-card *ngIf="item.employerOverview">
|
||||||
|
<ion-card-header> {{ 'jobs.employer' | translate }} </ion-card-header>
|
||||||
|
<ion-card-content>
|
||||||
|
<stapps-external-link
|
||||||
|
*ngIf="item.employerOverview.sameAs"
|
||||||
|
[link]="item.employerOverview.sameAs"
|
||||||
|
[text]="item.employerOverview.name"
|
||||||
|
></stapps-external-link>
|
||||||
|
<p *ngIf="!item.employerOverview.sameAs">{{ item.employerOverview.name }}</p>
|
||||||
|
<ion-img
|
||||||
|
*ngIf="item.employerOverview.image"
|
||||||
|
class="company-image"
|
||||||
|
src="{{ item.employerOverview.image }}"
|
||||||
|
></ion-img>
|
||||||
|
<p [innerHtml]="item.employerOverview.description"></p>
|
||||||
|
</ion-card-content>
|
||||||
|
</ion-card>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
.company-image {
|
||||||
|
width: 200px;
|
||||||
|
height: 100px;
|
||||||
|
margin: var(--spacing-md) 0 var(--spacing-md) 0;
|
||||||
|
object-position: left;
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019 StApps
|
||||||
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
* Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with
|
||||||
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
import {Component, Input} from '@angular/core';
|
||||||
|
import {SCJobPosting} from '@openstapps/core';
|
||||||
|
import {DataListItemComponent} from '../../list/data-list-item.component';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'stapps-job-posting-list-item',
|
||||||
|
templateUrl: 'job-posting-list-item.html',
|
||||||
|
styleUrls: ['job-posting-list-item.scss'],
|
||||||
|
})
|
||||||
|
export class JobPostingListItemComponent extends DataListItemComponent {
|
||||||
|
@Input() item: SCJobPosting;
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<!--
|
||||||
|
~ Copyright (C) 2022 StApps
|
||||||
|
~ This program is free software: you can redistribute it and/or modify it
|
||||||
|
~ under the terms of the GNU General Public License as published by the Free
|
||||||
|
~ Software Foundation, version 3.
|
||||||
|
~
|
||||||
|
~ This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
~ more details.
|
||||||
|
~
|
||||||
|
~ You should have received a copy of the GNU General Public License along with
|
||||||
|
~ this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<ion-grid>
|
||||||
|
<ion-row>
|
||||||
|
<ion-col>
|
||||||
|
<ion-label class="title">{{ 'name' | thingTranslate: item }}</ion-label>
|
||||||
|
<ion-label class="title-sub categories">
|
||||||
|
{{ 'categories' | thingTranslate: item | join: ', ' | titlecase }}
|
||||||
|
</ion-label>
|
||||||
|
<ion-label class="employer">{{ item.employerOverview?.name }}</ion-label>
|
||||||
|
</ion-col>
|
||||||
|
</ion-row>
|
||||||
|
</ion-grid>
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
// since we have three lines of information, clamp harder than usual
|
||||||
|
ion-col ion-label {
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
|
||||||
|
&.title-sub,
|
||||||
|
&.employer {
|
||||||
|
-webkit-line-clamp: 1 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ion-label.categories {
|
||||||
|
color: var(--ion-color-medium);
|
||||||
|
}
|
||||||
31
frontend/app/src/app/modules/jobs/jobs.module.ts
Normal file
31
frontend/app/src/app/modules/jobs/jobs.module.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import {NgModule} from '@angular/core';
|
||||||
|
import {CommonModule} from '@angular/common';
|
||||||
|
import {IonicModule} from '@ionic/angular';
|
||||||
|
import {TranslateModule} from '@ngx-translate/core';
|
||||||
|
import {MomentModule} from 'ngx-moment';
|
||||||
|
import {DataModule} from '../data/data.module';
|
||||||
|
import {UtilModule} from '../../util/util.module';
|
||||||
|
import {IonIconModule} from '../../util/ion-icon/ion-icon.module';
|
||||||
|
import {ConfigProvider} from '../config/config.provider';
|
||||||
|
import {ThingTranslateModule} from '../../translation/thing-translate.module';
|
||||||
|
import {RouterModule, Routes} from '@angular/router';
|
||||||
|
import {JobsPageComponent} from './page/jobs-page.component';
|
||||||
|
|
||||||
|
const jobsRoutes: Routes = [{path: 'jobs', component: JobsPageComponent}];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [JobsPageComponent],
|
||||||
|
imports: [
|
||||||
|
IonicModule.forRoot(),
|
||||||
|
ThingTranslateModule.forChild(),
|
||||||
|
TranslateModule.forChild(),
|
||||||
|
RouterModule.forChild(jobsRoutes),
|
||||||
|
IonIconModule,
|
||||||
|
CommonModule,
|
||||||
|
MomentModule,
|
||||||
|
DataModule,
|
||||||
|
UtilModule,
|
||||||
|
],
|
||||||
|
providers: [ConfigProvider],
|
||||||
|
})
|
||||||
|
export class JobModule {}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import {Component} from '@angular/core';
|
||||||
|
import {SCSearchFilter, SCThingType} from '@openstapps/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'stapps-jobs-page',
|
||||||
|
templateUrl: 'jobs-page.html',
|
||||||
|
styleUrls: ['jobs-page.scss'],
|
||||||
|
})
|
||||||
|
export class JobsPageComponent {
|
||||||
|
forcedFilter: SCSearchFilter = {
|
||||||
|
type: 'value',
|
||||||
|
arguments: {
|
||||||
|
field: 'type',
|
||||||
|
value: SCThingType.JobPosting,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
7
frontend/app/src/app/modules/jobs/page/jobs-page.html
Normal file
7
frontend/app/src/app/modules/jobs/page/jobs-page.html
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<stapps-search-page
|
||||||
|
[title]="'jobs.title' | translate"
|
||||||
|
[placeholder]="'jobs.placeholder' | translate"
|
||||||
|
[showDefaultData]="true"
|
||||||
|
[forcedFilter]="forcedFilter"
|
||||||
|
[backUrl]="'/'"
|
||||||
|
></stapps-search-page>
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
</a>
|
</a>
|
||||||
|
|
||||||
<ng-template #titleTemplate>
|
<ng-template #titleTemplate>
|
||||||
<ion-label class="section-headline">{{ title }} </ion-label>
|
<ion-label class="section-headline">{{ title }}</ion-label>
|
||||||
<ng-content select="[slot=subtitle]"></ng-content>
|
<ng-content select="[slot=subtitle]"></ng-content>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</ion-col>
|
</ion-col>
|
||||||
|
|||||||
@@ -98,6 +98,10 @@
|
|||||||
"title": "Aktuelles",
|
"title": "Aktuelles",
|
||||||
"moreNews": "Mehr Nachrichten"
|
"moreNews": "Mehr Nachrichten"
|
||||||
},
|
},
|
||||||
|
"jobs": {
|
||||||
|
"title": "Jobangebote",
|
||||||
|
"noJobs": "Es sind momentan keine Jobangebote verfügbar."
|
||||||
|
},
|
||||||
"schedule": {
|
"schedule": {
|
||||||
"title": "Nächste Einheit",
|
"title": "Nächste Einheit",
|
||||||
"noEvent": "Kein Eintrag gefunden",
|
"noEvent": "Kein Eintrag gefunden",
|
||||||
@@ -390,6 +394,11 @@
|
|||||||
"news": {
|
"news": {
|
||||||
"title": "Aktuelles"
|
"title": "Aktuelles"
|
||||||
},
|
},
|
||||||
|
"jobs": {
|
||||||
|
"title": "Jobangebote",
|
||||||
|
"placeholder": "Jobangebote",
|
||||||
|
"employer": "Arbeitgeber"
|
||||||
|
},
|
||||||
"canteens": {
|
"canteens": {
|
||||||
"title": "Mensa"
|
"title": "Mensa"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -98,6 +98,10 @@
|
|||||||
"title": "News",
|
"title": "News",
|
||||||
"moreNews": "More News"
|
"moreNews": "More News"
|
||||||
},
|
},
|
||||||
|
"jobs": {
|
||||||
|
"title": "Job postings",
|
||||||
|
"noJobs": "At the moment, there are not job postings available."
|
||||||
|
},
|
||||||
"schedule": {
|
"schedule": {
|
||||||
"title": "Next Unit",
|
"title": "Next Unit",
|
||||||
"noEvent": "No entry found",
|
"noEvent": "No entry found",
|
||||||
@@ -390,6 +394,11 @@
|
|||||||
"news": {
|
"news": {
|
||||||
"title": "News"
|
"title": "News"
|
||||||
},
|
},
|
||||||
|
"jobs": {
|
||||||
|
"title": "Job postings",
|
||||||
|
"placeholder": "Job Postings",
|
||||||
|
"employer": "Employer"
|
||||||
|
},
|
||||||
"canteens": {
|
"canteens": {
|
||||||
"title": "Canteens"
|
"title": "Canteens"
|
||||||
},
|
},
|
||||||
|
|||||||
Binary file not shown.
@@ -31,6 +31,7 @@ export * from './things/course-of-study.js';
|
|||||||
export * from './things/date-series.js';
|
export * from './things/date-series.js';
|
||||||
export * from './things/diff.js';
|
export * from './things/diff.js';
|
||||||
export * from './things/dish.js';
|
export * from './things/dish.js';
|
||||||
|
export * from './things/job-posting.js';
|
||||||
export * from './things/favorite.js';
|
export * from './things/favorite.js';
|
||||||
export * from './things/floor.js';
|
export * from './things/floor.js';
|
||||||
export * from './things/id-card.js';
|
export * from './things/id-card.js';
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ import {SCDiff, SCDiffMeta, SCDiffWithoutReferences} from './things/diff.js';
|
|||||||
import {SCDish, SCDishMeta, SCDishWithoutReferences} from './things/dish.js';
|
import {SCDish, SCDishMeta, SCDishWithoutReferences} from './things/dish.js';
|
||||||
import {SCFavorite, SCFavoriteMeta, SCFavoriteWithoutReferences} from './things/favorite.js';
|
import {SCFavorite, SCFavoriteMeta, SCFavoriteWithoutReferences} from './things/favorite.js';
|
||||||
import {SCFloor, SCFloorMeta, SCFloorWithoutReferences} from './things/floor.js';
|
import {SCFloor, SCFloorMeta, SCFloorWithoutReferences} from './things/floor.js';
|
||||||
|
import {SCIdCard, SCIdCardMeta, SCIdCardWithoutReferences} from './things/id-card.js';
|
||||||
|
import {SCJobPosting, SCJobPostingMeta, SCJobPostingWithoutReferences} from './things/job-posting.js';
|
||||||
import {SCMessage, SCMessageMeta, SCMessageWithoutReferences} from './things/message.js';
|
import {SCMessage, SCMessageMeta, SCMessageWithoutReferences} from './things/message.js';
|
||||||
import {SCOrganization, SCOrganizationMeta, SCOrganizationWithoutReferences} from './things/organization.js';
|
import {SCOrganization, SCOrganizationMeta, SCOrganizationWithoutReferences} from './things/organization.js';
|
||||||
import {SCPeriodical, SCPeriodicalMeta, SCPeriodicalWithoutReferences} from './things/periodical.js';
|
import {SCPeriodical, SCPeriodicalMeta, SCPeriodicalWithoutReferences} from './things/periodical.js';
|
||||||
@@ -62,7 +64,6 @@ import {SCTicket, SCTicketMeta, SCTicketWithoutReferences} from './things/ticket
|
|||||||
import {SCToDo, SCToDoMeta, SCToDoWithoutReferences} from './things/todo.js';
|
import {SCToDo, SCToDoMeta, SCToDoWithoutReferences} from './things/todo.js';
|
||||||
import {SCTour, SCTourMeta, SCTourWithoutReferences} from './things/tour.js';
|
import {SCTour, SCTourMeta, SCTourWithoutReferences} from './things/tour.js';
|
||||||
import {SCVideo, SCVideoMeta, SCVideoWithoutReferences} from './things/video.js';
|
import {SCVideo, SCVideoMeta, SCVideoWithoutReferences} from './things/video.js';
|
||||||
import {SCIdCard, SCIdCardMeta, SCIdCardWithoutReferences} from './things/id-card.js';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A map of things, from type to meta data
|
* A map of things, from type to meta data
|
||||||
@@ -98,6 +99,7 @@ export const SCClasses: {[K in SCThingType]: object} = {
|
|||||||
'tour': SCTourMeta,
|
'tour': SCTourMeta,
|
||||||
'video': SCVideoMeta,
|
'video': SCVideoMeta,
|
||||||
'certification': SCCertificationMeta,
|
'certification': SCCertificationMeta,
|
||||||
|
'job posting': SCJobPostingMeta,
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SCIndexableThings =
|
export type SCIndexableThings =
|
||||||
@@ -127,7 +129,8 @@ export type SCIndexableThings =
|
|||||||
| SCTicket
|
| SCTicket
|
||||||
| SCToDo
|
| SCToDo
|
||||||
| SCTour
|
| SCTour
|
||||||
| SCVideo;
|
| SCVideo
|
||||||
|
| SCJobPosting;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An object that exists in the StAppsCore
|
* An object that exists in the StAppsCore
|
||||||
@@ -172,6 +175,8 @@ export type SCAssociatedThingWithoutReferences<THING extends SCThings> = THING e
|
|||||||
? SCFloorWithoutReferences
|
? SCFloorWithoutReferences
|
||||||
: THING extends SCIdCard
|
: THING extends SCIdCard
|
||||||
? SCIdCardWithoutReferences
|
? SCIdCardWithoutReferences
|
||||||
|
: THING extends SCJobPosting
|
||||||
|
? SCJobPostingWithoutReferences
|
||||||
: THING extends SCMessage
|
: THING extends SCMessage
|
||||||
? SCMessageWithoutReferences
|
? SCMessageWithoutReferences
|
||||||
: THING extends SCOrganization
|
: THING extends SCOrganization
|
||||||
@@ -231,6 +236,8 @@ export type SCAssociatedThing<THING extends SCThings> = THING extends SCAssessme
|
|||||||
? SCDiff
|
? SCDiff
|
||||||
: THING extends SCDishWithoutReferences
|
: THING extends SCDishWithoutReferences
|
||||||
? SCDish
|
? SCDish
|
||||||
|
: THING extends SCJobPostingWithoutReferences
|
||||||
|
? SCJobPosting
|
||||||
: THING extends SCFavoriteWithoutReferences
|
: THING extends SCFavoriteWithoutReferences
|
||||||
? SCFavorite
|
? SCFavorite
|
||||||
: THING extends SCFloorWithoutReferences
|
: THING extends SCFloorWithoutReferences
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ export enum SCThingType {
|
|||||||
ToDo = 'todo',
|
ToDo = 'todo',
|
||||||
Tour = 'tour',
|
Tour = 'tour',
|
||||||
Video = 'video',
|
Video = 'video',
|
||||||
|
JobPosting = 'job posting',
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
108
packages/core/src/things/job-posting.ts
Normal file
108
packages/core/src/things/job-posting.ts
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
import {SCInPlace} from './abstract/place.js';
|
||||||
|
import {
|
||||||
|
SCThingWithCategories,
|
||||||
|
SCThingWithCategoriesSpecificValues,
|
||||||
|
SCThingWithCategoriesWithoutReferences,
|
||||||
|
SCThingWithCategoriesWithoutReferencesMeta,
|
||||||
|
} from './abstract/thing-with-categories.js';
|
||||||
|
import {SCThingMeta, SCThingType} from './abstract/thing.js';
|
||||||
|
import {SCOrganizationWithoutReferences} from './organization.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* categories of a job posting
|
||||||
|
*/
|
||||||
|
export type SCJobCategories =
|
||||||
|
| 'law'
|
||||||
|
| 'business'
|
||||||
|
| 'natural'
|
||||||
|
| 'it'
|
||||||
|
| 'education'
|
||||||
|
| 'other'
|
||||||
|
| 'arts'
|
||||||
|
| 'social'
|
||||||
|
| 'engineering'
|
||||||
|
| 'communication'
|
||||||
|
| 'medical';
|
||||||
|
|
||||||
|
export interface SCJobPostingWithoutReferences
|
||||||
|
extends SCThingWithCategoriesWithoutReferences<SCJobCategories, SCThingWithCategoriesSpecificValues>,
|
||||||
|
SCInPlace {
|
||||||
|
/**
|
||||||
|
* Type of a job posting
|
||||||
|
*/
|
||||||
|
type: SCThingType.JobPosting;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A JobPosting
|
||||||
|
* @validatable
|
||||||
|
* @indexable
|
||||||
|
*/
|
||||||
|
export interface SCJobPosting
|
||||||
|
extends SCThingWithCategories<SCJobCategories, SCThingWithCategoriesSpecificValues>,
|
||||||
|
SCJobPostingWithoutReferences {
|
||||||
|
/**
|
||||||
|
* A description of the employer
|
||||||
|
* @text
|
||||||
|
*/
|
||||||
|
employerOverview?: SCOrganizationWithoutReferences;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A JobPosting
|
||||||
|
*/
|
||||||
|
type: SCThingType.JobPosting;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SCJobPostingMeta extends SCThingMeta {
|
||||||
|
fieldTranslations = {
|
||||||
|
de: {
|
||||||
|
...new SCThingMeta().fieldTranslations.de,
|
||||||
|
...new SCThingWithCategoriesWithoutReferencesMeta().fieldTranslations.de,
|
||||||
|
employerOverview: 'Arbeitgeberübersicht',
|
||||||
|
},
|
||||||
|
en: {
|
||||||
|
...new SCThingMeta().fieldTranslations.en,
|
||||||
|
...new SCThingWithCategoriesWithoutReferencesMeta().fieldTranslations.en,
|
||||||
|
employerOverview: 'employer overview',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
fieldValueTranslations = {
|
||||||
|
de: {
|
||||||
|
...new SCThingMeta().fieldValueTranslations.de,
|
||||||
|
...new SCThingWithCategoriesWithoutReferencesMeta().fieldValueTranslations.de,
|
||||||
|
type: 'Job Angebot',
|
||||||
|
categories: {
|
||||||
|
law: 'Recht',
|
||||||
|
business: 'Business',
|
||||||
|
natural: 'Ökologisch',
|
||||||
|
it: 'IT',
|
||||||
|
education: 'Bildung',
|
||||||
|
other: 'Andere',
|
||||||
|
arts: 'Künste',
|
||||||
|
social: 'Sozial',
|
||||||
|
engineering: 'Ingenieurswesen',
|
||||||
|
communication: 'Kommunikation',
|
||||||
|
medical: 'Medizin',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
en: {
|
||||||
|
...new SCThingMeta().fieldValueTranslations.en,
|
||||||
|
...new SCThingWithCategoriesWithoutReferencesMeta().fieldValueTranslations.en,
|
||||||
|
type: SCThingType.JobPosting,
|
||||||
|
categories: {
|
||||||
|
law: 'law',
|
||||||
|
business: 'business',
|
||||||
|
natural: 'natural',
|
||||||
|
it: 'it',
|
||||||
|
education: 'education',
|
||||||
|
other: 'other',
|
||||||
|
arts: 'arts',
|
||||||
|
social: 'social',
|
||||||
|
engineering: 'engineering',
|
||||||
|
communication: 'communication',
|
||||||
|
medical: 'medical',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
47
packages/core/test/resources/indexable/JobPosting1.json
Normal file
47
packages/core/test/resources/indexable/JobPosting1.json
Normal file
File diff suppressed because one or more lines are too long
2
pnpm-lock.yaml
generated
2
pnpm-lock.yaml
generated
@@ -12314,6 +12314,7 @@ packages:
|
|||||||
|
|
||||||
/immediate@3.0.6:
|
/immediate@3.0.6:
|
||||||
resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==}
|
resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==}
|
||||||
|
requiresBuild: true
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/immutable@4.3.1:
|
/immutable@4.3.1:
|
||||||
@@ -13421,6 +13422,7 @@ packages:
|
|||||||
|
|
||||||
/lie@3.1.1:
|
/lie@3.1.1:
|
||||||
resolution: {integrity: sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==}
|
resolution: {integrity: sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==}
|
||||||
|
requiresBuild: true
|
||||||
dependencies:
|
dependencies:
|
||||||
immediate: 3.0.6
|
immediate: 3.0.6
|
||||||
dev: false
|
dev: false
|
||||||
|
|||||||
Reference in New Issue
Block a user