feat: job portal

fix: disable function scoping lint rule
fix: outdated contributing docs for adding SCThings
This commit is contained in:
theld
2023-10-02 20:34:49 +00:00
committed by Thea Schöbl
parent f3ba8af051
commit 06b8ca109e
42 changed files with 626 additions and 65 deletions

View File

@@ -0,0 +1,7 @@
---
'@openstapps/backend': minor
'@openstapps/core': minor
'@openstapps/app': minor
---
Add job portal feature

View File

@@ -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

View File

@@ -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',

View File

@@ -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"/>

View File

@@ -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"/>

View File

@@ -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',

View File

@@ -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",

View File

@@ -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');
}); });
}); });

View File

@@ -35,6 +35,7 @@ const config: IconConfig = {
'settings', 'settings',
'info', 'info',
'rate_review', 'rate_review',
'work',
], ],
}, },
codePoints: { codePoints: {

View File

@@ -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,

View File

@@ -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) {

View File

@@ -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>

View File

@@ -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],
}) })

View File

@@ -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>

View File

@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -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,
}; };
} }

View File

@@ -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`,
}; };

View File

@@ -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,

View File

@@ -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>

View File

@@ -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>

View File

@@ -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({

View File

@@ -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;
}

View File

@@ -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>

View File

@@ -0,0 +1,6 @@
.company-image {
width: 200px;
height: 100px;
margin: var(--spacing-md) 0 var(--spacing-md) 0;
object-position: left;
}

View File

@@ -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;
}

View File

@@ -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>

View File

@@ -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);
}

View 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 {}

View File

@@ -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,
},
};
}

View File

@@ -0,0 +1,7 @@
<stapps-search-page
[title]="'jobs.title' | translate"
[placeholder]="'jobs.placeholder' | translate"
[showDefaultData]="true"
[forcedFilter]="forcedFilter"
[backUrl]="'/'"
></stapps-search-page>

View File

@@ -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>

View File

@@ -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"
}, },

View File

@@ -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"
}, },

View File

@@ -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';

View File

@@ -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

View File

@@ -53,6 +53,7 @@ export enum SCThingType {
ToDo = 'todo', ToDo = 'todo',
Tour = 'tour', Tour = 'tour',
Video = 'video', Video = 'video',
JobPosting = 'job posting',
} }
/** /**

View 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',
},
},
};
}

File diff suppressed because one or more lines are too long

2
pnpm-lock.yaml generated
View File

@@ -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