@studyportals/fawkes 7.3.5 → 7.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/structured-data/ProgrammeStructuredDataFactory.d.ts +1 -1
- package/dist/src/structured-data/ProgrammeStructuredDataFactory.js +5 -2
- package/dist/src/structured-data/SearchStructuredDataFactory.d.ts +7 -8
- package/dist/src/structured-data/SearchStructuredDataFactory.js +41 -51
- package/package.json +4 -4
- package/dist/structured-data/index.d.ts +0 -1
- package/dist/structured-data/index.js +0 -1
- package/dist/structured-markup-seo/index.d.ts +0 -1
- package/dist/structured-markup-seo/index.js +0 -1
|
@@ -11,7 +11,7 @@ export declare class ProgrammeStructuredDataFactory extends SearchStructuredData
|
|
|
11
11
|
private regionalDataPriority;
|
|
12
12
|
protected getRating(card: IProgrammeCard): ReviewRatingDTO | undefined;
|
|
13
13
|
protected getOfferData(card: IProgrammeCard): Promise<OfferDTO | undefined>;
|
|
14
|
-
protected buildStructuredDataForCard(entity: EntityDTO<IProgrammeCard>): Course;
|
|
14
|
+
protected buildStructuredDataForCard(entity: EntityDTO<IProgrammeCard>): Course | undefined;
|
|
15
15
|
private getCourseMode;
|
|
16
16
|
private getCourseWorkload;
|
|
17
17
|
private getCourseStartDate;
|
|
@@ -35,12 +35,15 @@ export class ProgrammeStructuredDataFactory extends SearchStructuredDataFactory
|
|
|
35
35
|
return;
|
|
36
36
|
}
|
|
37
37
|
return {
|
|
38
|
-
price: await this.currencyConversionService.convert(tuition.getAmount(), tuition.getCurrency(), 'USD'),
|
|
38
|
+
price: Math.round(await this.currencyConversionService.convert(tuition.getAmount(), tuition.getCurrency(), 'USD')),
|
|
39
39
|
category: 'tuition',
|
|
40
40
|
validFrom: timing ? timing.getStartDate().getDate() : undefined
|
|
41
41
|
};
|
|
42
42
|
}
|
|
43
43
|
buildStructuredDataForCard(entity) {
|
|
44
|
+
const offer = this.constructOffer(entity.offer);
|
|
45
|
+
if (!offer)
|
|
46
|
+
return undefined;
|
|
44
47
|
const card = entity.card;
|
|
45
48
|
return {
|
|
46
49
|
"@type": "Course",
|
|
@@ -52,7 +55,7 @@ export class ProgrammeStructuredDataFactory extends SearchStructuredDataFactory
|
|
|
52
55
|
"name": card.getUniversityLink().getDescription(),
|
|
53
56
|
"url": `https://${window.location.host}${card.getUniversityLink().getUrl()}`
|
|
54
57
|
},
|
|
55
|
-
"offers":
|
|
58
|
+
"offers": offer,
|
|
56
59
|
"aggregateRating": this.constructAggregateRating(entity.reviewRating),
|
|
57
60
|
"hasCourseInstance": {
|
|
58
61
|
"@type": "CourseInstance",
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { SearchResultsPage, WithContext, Offer, AggregateRating, Thing, FAQPage } from
|
|
2
|
-
import { ReviewRatingDTO } from
|
|
3
|
-
import { OfferDTO } from
|
|
4
|
-
import { EntityDTO } from
|
|
5
|
-
import { FAQItemDto } from
|
|
6
|
-
import { BreadcrumbDTO } from
|
|
1
|
+
import { SearchResultsPage, WithContext, Offer, AggregateRating, Thing, FAQPage } from 'schema-dts';
|
|
2
|
+
import { ReviewRatingDTO } from './dto/ReviewRatingDTO';
|
|
3
|
+
import { OfferDTO } from './dto/OfferDTO';
|
|
4
|
+
import { EntityDTO } from './dto/EntityDTO';
|
|
5
|
+
import { FAQItemDto } from './dto/FAQItemDto';
|
|
6
|
+
import { BreadcrumbDTO } from './dto/BreadcrumbDTO';
|
|
7
7
|
export declare abstract class SearchStructuredDataFactory<TCard> {
|
|
8
8
|
buildStructuredData(title: string, description: string, cards: TCard[], faqItems?: FAQItemDto[], breadcrumbs?: BreadcrumbDTO[]): Promise<WithContext<SearchResultsPage>>;
|
|
9
|
-
protected abstract buildStructuredDataForCard(entity: EntityDTO<TCard>): Thing;
|
|
9
|
+
protected abstract buildStructuredDataForCard(entity: EntityDTO<TCard>): Thing | undefined;
|
|
10
10
|
/**
|
|
11
11
|
* Get the rating for a card.
|
|
12
12
|
* This method should be overridden by subclasses to provide the specific rating logic.
|
|
@@ -25,7 +25,6 @@ export declare abstract class SearchStructuredDataFactory<TCard> {
|
|
|
25
25
|
* @protected
|
|
26
26
|
*/
|
|
27
27
|
protected getOfferData(card: TCard): Promise<OfferDTO | undefined>;
|
|
28
|
-
private constructOptionalPageAggregateRating;
|
|
29
28
|
private constructOptionalPageOffers;
|
|
30
29
|
protected constructOptionalFaqPage(faqItems: FAQItemDto[]): FAQPage | undefined;
|
|
31
30
|
private constructOptionalBreadcrumbs;
|
|
@@ -12,14 +12,16 @@ export class SearchStructuredDataFactory {
|
|
|
12
12
|
offersData.push(offer);
|
|
13
13
|
entities.push({ card, offer, reviewRating });
|
|
14
14
|
}
|
|
15
|
+
const mainEntities = entities
|
|
16
|
+
.map(entity => this.buildStructuredDataForCard(entity))
|
|
17
|
+
.filter(entity => entity !== undefined);
|
|
15
18
|
const data = {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
'@context': 'https://schema.org',
|
|
20
|
+
'@type': 'SearchResultsPage',
|
|
21
|
+
'name': title,
|
|
19
22
|
description,
|
|
20
|
-
|
|
23
|
+
'mainEntity': mainEntities
|
|
21
24
|
};
|
|
22
|
-
data.aggregateRating = this.constructOptionalPageAggregateRating(ratings);
|
|
23
25
|
data.offers = this.constructOptionalPageOffers(offersData);
|
|
24
26
|
data.hasPart = this.constructOptionalFaqPage(faqItems);
|
|
25
27
|
data.breadcrumb = this.constructOptionalBreadcrumbs(breadcrumbs);
|
|
@@ -49,18 +51,6 @@ export class SearchStructuredDataFactory {
|
|
|
49
51
|
async getOfferData(card) {
|
|
50
52
|
return undefined;
|
|
51
53
|
}
|
|
52
|
-
constructOptionalPageAggregateRating(ratings) {
|
|
53
|
-
if (ratings.length <= 0)
|
|
54
|
-
return;
|
|
55
|
-
let totalQuantity = 0;
|
|
56
|
-
let totalRatingValue = 0;
|
|
57
|
-
for (const rating of ratings) {
|
|
58
|
-
totalQuantity += rating.quantity;
|
|
59
|
-
totalRatingValue += rating.rating;
|
|
60
|
-
}
|
|
61
|
-
const totalAverageRating = Math.round((totalRatingValue / ratings.length) * 10) / 10;
|
|
62
|
-
return this.constructAggregateRating({ rating: totalAverageRating, quantity: totalQuantity });
|
|
63
|
-
}
|
|
64
54
|
constructOptionalPageOffers(offersData) {
|
|
65
55
|
if (offersData.length <= 0)
|
|
66
56
|
return;
|
|
@@ -77,15 +67,15 @@ export class SearchStructuredDataFactory {
|
|
|
77
67
|
totalPrice += offer.price;
|
|
78
68
|
}
|
|
79
69
|
return {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
70
|
+
'@type': 'AggregateOffer',
|
|
71
|
+
'lowPrice': Math.max(minPrice, 0),
|
|
72
|
+
'highPrice': maxPrice,
|
|
73
|
+
'priceCurrency': 'USD',
|
|
74
|
+
'offerCount': offersData.length,
|
|
75
|
+
'priceSpecification': {
|
|
76
|
+
'@type': 'PriceSpecification',
|
|
77
|
+
'price': Math.round(totalPrice / offersData.length),
|
|
78
|
+
'priceCurrency': 'USD'
|
|
89
79
|
}
|
|
90
80
|
};
|
|
91
81
|
}
|
|
@@ -93,13 +83,13 @@ export class SearchStructuredDataFactory {
|
|
|
93
83
|
if (faqItems.length <= 0)
|
|
94
84
|
return;
|
|
95
85
|
return {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
86
|
+
'@type': 'FAQPage',
|
|
87
|
+
'mainEntity': faqItems.map(item => ({
|
|
88
|
+
'@type': 'Question',
|
|
89
|
+
'name': item.question,
|
|
90
|
+
'acceptedAnswer': {
|
|
91
|
+
'@type': 'Answer',
|
|
92
|
+
'text': this.stripHtmlWithListRetain(item.answer)
|
|
103
93
|
}
|
|
104
94
|
}))
|
|
105
95
|
};
|
|
@@ -108,13 +98,13 @@ export class SearchStructuredDataFactory {
|
|
|
108
98
|
if (breadcrumbs.length <= 0)
|
|
109
99
|
return;
|
|
110
100
|
return {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
101
|
+
'@type': 'BreadcrumbList',
|
|
102
|
+
'itemListElement': breadcrumbs.map((breadcrumb) => ({
|
|
103
|
+
'@type': 'ListItem',
|
|
104
|
+
'@id': `${breadcrumb.url}#listitem#thing`,
|
|
105
|
+
'name': breadcrumb.name,
|
|
106
|
+
'position': breadcrumb.position,
|
|
107
|
+
'item': breadcrumb.url
|
|
118
108
|
}))
|
|
119
109
|
};
|
|
120
110
|
}
|
|
@@ -122,23 +112,23 @@ export class SearchStructuredDataFactory {
|
|
|
122
112
|
if (!reviewRating)
|
|
123
113
|
return;
|
|
124
114
|
return {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
115
|
+
'@type': 'AggregateRating',
|
|
116
|
+
'ratingValue': reviewRating.rating,
|
|
117
|
+
'reviewCount': reviewRating.quantity,
|
|
118
|
+
'bestRating': 5,
|
|
119
|
+
'worstRating': 1
|
|
130
120
|
};
|
|
131
121
|
}
|
|
132
122
|
constructOffer(offerData) {
|
|
133
123
|
if (!offerData)
|
|
134
124
|
return;
|
|
135
125
|
return {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
126
|
+
'@type': 'Offer',
|
|
127
|
+
'price': offerData.price,
|
|
128
|
+
'priceCurrency': 'USD',
|
|
129
|
+
'availability': 'https://schema.org/InStock',
|
|
130
|
+
'validFrom': offerData.validFrom ? offerData.validFrom.toISOString().split('T')[0] : undefined,
|
|
131
|
+
'category': 'tuition'
|
|
142
132
|
};
|
|
143
133
|
}
|
|
144
134
|
stripHtmlWithListRetain(html) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@studyportals/fawkes",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.4.0",
|
|
4
4
|
"description": "A package to centralize SEO related logic for SBLP and Sitemap Generator.",
|
|
5
5
|
"files": [
|
|
6
6
|
"./dist"
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"publish-major": "npm run prepare-deployment && npm version major && npm publish",
|
|
16
16
|
"publish-beta": "npm run prepare-deployment && npm version prerelease && npm publish --tag beta --access=public",
|
|
17
17
|
"publish-patch": "npm run prepare-deployment && npm version patch && npm publish",
|
|
18
|
-
"publish-minor": "npm run prepare-deployment && npm
|
|
18
|
+
"publish-minor": "npm run prepare-deployment && npm publish",
|
|
19
19
|
"prepare": "husky install",
|
|
20
20
|
"test": "vitest run --coverage",
|
|
21
21
|
"test:dev": "vitest --coverage tests/programmes",
|
|
@@ -90,9 +90,9 @@
|
|
|
90
90
|
"ts-loader": "^9.5.2"
|
|
91
91
|
},
|
|
92
92
|
"dependencies": {
|
|
93
|
-
"@studyportals/domain-client": "^6.
|
|
93
|
+
"@studyportals/domain-client": "^6.3.0",
|
|
94
94
|
"@studyportals/ranking-api-interface": "^1.3.12",
|
|
95
|
-
"@studyportals/search-filters": "^
|
|
95
|
+
"@studyportals/search-filters": "^5.1.0",
|
|
96
96
|
"@studyportals/static-domain-data": "^6.1.0"
|
|
97
97
|
},
|
|
98
98
|
"optionalDependencies": {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from '../src/structured-data';
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from '../src/structured-data';
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from '../src/structured-data';
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from '../src/structured-data';
|