@getvitops/astro 0.1.1 → 0.2.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.
@@ -0,0 +1,115 @@
1
+ ---
2
+ export interface Props {
3
+ name: string;
4
+ description?: string;
5
+ startDate: string;
6
+ endDate?: string;
7
+ eventStatus?: 'EventScheduled' | 'EventCancelled' | 'EventMovedOnline' | 'EventPostponed' | 'EventRescheduled';
8
+ eventAttendanceMode?: 'OfflineEventAttendanceMode' | 'OnlineEventAttendanceMode' | 'MixedEventAttendanceMode';
9
+ location?: {
10
+ type: 'Place' | 'VirtualLocation';
11
+ name?: string;
12
+ address?: {
13
+ streetAddress?: string;
14
+ addressLocality?: string;
15
+ addressRegion?: string;
16
+ postalCode?: string;
17
+ addressCountry?: string;
18
+ };
19
+ url?: string;
20
+ };
21
+ image?: string | string[];
22
+ performer?: {
23
+ type: 'Person' | 'PerformingGroup' | 'Organization';
24
+ name: string;
25
+ }[];
26
+ organizer?: {
27
+ type: 'Person' | 'Organization';
28
+ name: string;
29
+ url?: string;
30
+ };
31
+ offers?: {
32
+ url?: string;
33
+ price?: number | string;
34
+ priceCurrency?: string;
35
+ availability?: 'InStock' | 'SoldOut' | 'PreOrder';
36
+ validFrom?: string;
37
+ }[];
38
+ previousStartDate?: string;
39
+ }
40
+
41
+ const {
42
+ name,
43
+ description,
44
+ startDate,
45
+ endDate,
46
+ eventStatus = 'EventScheduled',
47
+ eventAttendanceMode = 'OfflineEventAttendanceMode',
48
+ location,
49
+ image,
50
+ performer,
51
+ organizer,
52
+ offers,
53
+ previousStartDate,
54
+ } = Astro.props;
55
+
56
+ const jsonLd = {
57
+ '@context': 'https://schema.org',
58
+ '@type': 'Event',
59
+ name,
60
+ ...(description && { description }),
61
+ startDate,
62
+ ...(endDate && { endDate }),
63
+ eventStatus: `https://schema.org/${eventStatus}`,
64
+ eventAttendanceMode: `https://schema.org/${eventAttendanceMode}`,
65
+ ...(location && {
66
+ location: location.type === 'VirtualLocation'
67
+ ? {
68
+ '@type': 'VirtualLocation',
69
+ url: location.url,
70
+ }
71
+ : {
72
+ '@type': 'Place',
73
+ ...(location.name && { name: location.name }),
74
+ ...(location.address && {
75
+ address: {
76
+ '@type': 'PostalAddress',
77
+ ...(location.address.streetAddress && { streetAddress: location.address.streetAddress }),
78
+ ...(location.address.addressLocality && { addressLocality: location.address.addressLocality }),
79
+ ...(location.address.addressRegion && { addressRegion: location.address.addressRegion }),
80
+ ...(location.address.postalCode && { postalCode: location.address.postalCode }),
81
+ ...(location.address.addressCountry && { addressCountry: location.address.addressCountry }),
82
+ },
83
+ }),
84
+ },
85
+ }),
86
+ ...(image && { image: Array.isArray(image) ? image : [image] }),
87
+ ...(performer && {
88
+ performer: performer.map(p => ({
89
+ '@type': p.type,
90
+ name: p.name,
91
+ })),
92
+ }),
93
+ ...(organizer && {
94
+ organizer: {
95
+ '@type': organizer.type,
96
+ name: organizer.name,
97
+ ...(organizer.url && { url: organizer.url }),
98
+ },
99
+ }),
100
+ ...(offers && {
101
+ offers: offers.map(offer => ({
102
+ '@type': 'Offer',
103
+ ...(offer.url && { url: offer.url }),
104
+ ...(offer.price !== undefined && { price: offer.price }),
105
+ ...(offer.priceCurrency && { priceCurrency: offer.priceCurrency }),
106
+ ...(offer.availability && { availability: `https://schema.org/${offer.availability}` }),
107
+ ...(offer.validFrom && { validFrom: offer.validFrom }),
108
+ })),
109
+ }),
110
+ ...(previousStartDate && { previousStartDate }),
111
+ };
112
+ ---
113
+
114
+ <script is:inline type="application/ld+json" set:html={JSON.stringify(jsonLd)} />
115
+ <slot />
@@ -0,0 +1,34 @@
1
+ ---
2
+ /**
3
+ * FAQ Schema Component
4
+ *
5
+ * NOTE: Google recommends FAQ markup only for authoritative government
6
+ * and health sites. For other sites, consider using QAPage schema instead.
7
+ */
8
+ export interface FAQItem {
9
+ question: string;
10
+ answer: string;
11
+ }
12
+
13
+ export interface Props {
14
+ items: FAQItem[];
15
+ }
16
+
17
+ const { items } = Astro.props;
18
+
19
+ const jsonLd = {
20
+ '@context': 'https://schema.org',
21
+ '@type': 'FAQPage',
22
+ mainEntity: items.map(item => ({
23
+ '@type': 'Question',
24
+ name: item.question,
25
+ acceptedAnswer: {
26
+ '@type': 'Answer',
27
+ text: item.answer,
28
+ },
29
+ })),
30
+ };
31
+ ---
32
+
33
+ <script is:inline type="application/ld+json" set:html={JSON.stringify(jsonLd)} />
34
+ <slot />
@@ -0,0 +1,84 @@
1
+ ---
2
+ export interface Props {
3
+ claimReviewed: string;
4
+ url: string;
5
+ author: {
6
+ name: string;
7
+ url?: string;
8
+ };
9
+ datePublished: string;
10
+ itemReviewed?: {
11
+ type: 'Claim' | 'CreativeWork';
12
+ author?: {
13
+ type: 'Person' | 'Organization';
14
+ name: string;
15
+ url?: string;
16
+ };
17
+ datePublished?: string;
18
+ appearance?: {
19
+ url: string;
20
+ headline?: string;
21
+ }[];
22
+ };
23
+ reviewRating: {
24
+ ratingValue: number;
25
+ bestRating: number;
26
+ worstRating?: number;
27
+ alternateName: string;
28
+ ratingExplanation?: string;
29
+ };
30
+ }
31
+
32
+ const {
33
+ claimReviewed,
34
+ url,
35
+ author,
36
+ datePublished,
37
+ itemReviewed,
38
+ reviewRating,
39
+ } = Astro.props;
40
+
41
+ const jsonLd = {
42
+ '@context': 'https://schema.org',
43
+ '@type': 'ClaimReview',
44
+ claimReviewed,
45
+ url,
46
+ author: {
47
+ '@type': 'Organization',
48
+ name: author.name,
49
+ ...(author.url && { url: author.url }),
50
+ },
51
+ datePublished,
52
+ ...(itemReviewed && {
53
+ itemReviewed: {
54
+ '@type': itemReviewed.type,
55
+ ...(itemReviewed.author && {
56
+ author: {
57
+ '@type': itemReviewed.author.type,
58
+ name: itemReviewed.author.name,
59
+ ...(itemReviewed.author.url && { url: itemReviewed.author.url }),
60
+ },
61
+ }),
62
+ ...(itemReviewed.datePublished && { datePublished: itemReviewed.datePublished }),
63
+ ...(itemReviewed.appearance && {
64
+ appearance: itemReviewed.appearance.map(a => ({
65
+ '@type': 'CreativeWork',
66
+ url: a.url,
67
+ ...(a.headline && { headline: a.headline }),
68
+ })),
69
+ }),
70
+ },
71
+ }),
72
+ reviewRating: {
73
+ '@type': 'Rating',
74
+ ratingValue: reviewRating.ratingValue,
75
+ bestRating: reviewRating.bestRating,
76
+ worstRating: reviewRating.worstRating ?? 1,
77
+ alternateName: reviewRating.alternateName,
78
+ ...(reviewRating.ratingExplanation && { ratingExplanation: reviewRating.ratingExplanation }),
79
+ },
80
+ };
81
+ ---
82
+
83
+ <script is:inline type="application/ld+json" set:html={JSON.stringify(jsonLd)} />
84
+ <slot />
@@ -0,0 +1,56 @@
1
+ ---
2
+ export interface Props {
3
+ contentUrl: string;
4
+ creditText?: string;
5
+ creator?: {
6
+ name: string;
7
+ type?: 'Person' | 'Organization';
8
+ };
9
+ copyrightNotice?: string;
10
+ license?: string;
11
+ acquireLicensePage?: string;
12
+ caption?: string;
13
+ representativeOfPage?: boolean;
14
+ datePublished?: string;
15
+ description?: string;
16
+ name?: string;
17
+ }
18
+
19
+ const {
20
+ contentUrl,
21
+ creditText,
22
+ creator,
23
+ copyrightNotice,
24
+ license,
25
+ acquireLicensePage,
26
+ caption,
27
+ representativeOfPage,
28
+ datePublished,
29
+ description,
30
+ name,
31
+ } = Astro.props;
32
+
33
+ const jsonLd = {
34
+ '@context': 'https://schema.org',
35
+ '@type': 'ImageObject',
36
+ contentUrl,
37
+ ...(creditText && { creditText }),
38
+ ...(creator && {
39
+ creator: {
40
+ '@type': creator.type || 'Person',
41
+ name: creator.name,
42
+ },
43
+ }),
44
+ ...(copyrightNotice && { copyrightNotice }),
45
+ ...(license && { license }),
46
+ ...(acquireLicensePage && { acquireLicensePage }),
47
+ ...(caption && { caption }),
48
+ ...(representativeOfPage !== undefined && { representativeOfPage }),
49
+ ...(datePublished && { datePublished }),
50
+ ...(description && { description }),
51
+ ...(name && { name }),
52
+ };
53
+ ---
54
+
55
+ <script is:inline type="application/ld+json" set:html={JSON.stringify(jsonLd)} />
56
+ <slot />
@@ -0,0 +1,138 @@
1
+ ---
2
+ export interface Props {
3
+ title: string;
4
+ description: string;
5
+ datePosted: string;
6
+ validThrough?: string;
7
+ employmentType?: ('FULL_TIME' | 'PART_TIME' | 'CONTRACTOR' | 'TEMPORARY' | 'INTERN' | 'VOLUNTEER' | 'PER_DIEM' | 'OTHER')[];
8
+ hiringOrganization: {
9
+ name: string;
10
+ sameAs?: string;
11
+ logo?: string;
12
+ };
13
+ jobLocation?: {
14
+ streetAddress?: string;
15
+ addressLocality: string;
16
+ addressRegion?: string;
17
+ postalCode?: string;
18
+ addressCountry: string;
19
+ };
20
+ jobLocationType?: 'TELECOMMUTE';
21
+ applicantLocationRequirements?: {
22
+ type: 'Country' | 'State' | 'City';
23
+ name: string;
24
+ }[];
25
+ baseSalary?: {
26
+ currency: string;
27
+ value: number | { minValue: number; maxValue: number };
28
+ unitText: 'HOUR' | 'DAY' | 'WEEK' | 'MONTH' | 'YEAR';
29
+ };
30
+ identifier?: {
31
+ name: string;
32
+ value: string;
33
+ };
34
+ directApply?: boolean;
35
+ experienceRequirements?: string;
36
+ educationRequirements?: string;
37
+ skills?: string[];
38
+ qualifications?: string[];
39
+ responsibilities?: string[];
40
+ industry?: string;
41
+ occupationalCategory?: string;
42
+ }
43
+
44
+ const {
45
+ title,
46
+ description,
47
+ datePosted,
48
+ validThrough,
49
+ employmentType,
50
+ hiringOrganization,
51
+ jobLocation,
52
+ jobLocationType,
53
+ applicantLocationRequirements,
54
+ baseSalary,
55
+ identifier,
56
+ directApply,
57
+ experienceRequirements,
58
+ educationRequirements,
59
+ skills,
60
+ qualifications,
61
+ responsibilities,
62
+ industry,
63
+ occupationalCategory,
64
+ } = Astro.props;
65
+
66
+ const jsonLd = {
67
+ '@context': 'https://schema.org',
68
+ '@type': 'JobPosting',
69
+ title,
70
+ description,
71
+ datePosted,
72
+ ...(validThrough && { validThrough }),
73
+ ...(employmentType && { employmentType }),
74
+ hiringOrganization: {
75
+ '@type': 'Organization',
76
+ name: hiringOrganization.name,
77
+ ...(hiringOrganization.sameAs && { sameAs: hiringOrganization.sameAs }),
78
+ ...(hiringOrganization.logo && { logo: hiringOrganization.logo }),
79
+ },
80
+ ...(jobLocation && {
81
+ jobLocation: {
82
+ '@type': 'Place',
83
+ address: {
84
+ '@type': 'PostalAddress',
85
+ ...(jobLocation.streetAddress && { streetAddress: jobLocation.streetAddress }),
86
+ addressLocality: jobLocation.addressLocality,
87
+ ...(jobLocation.addressRegion && { addressRegion: jobLocation.addressRegion }),
88
+ ...(jobLocation.postalCode && { postalCode: jobLocation.postalCode }),
89
+ addressCountry: jobLocation.addressCountry,
90
+ },
91
+ },
92
+ }),
93
+ ...(jobLocationType && { jobLocationType }),
94
+ ...(applicantLocationRequirements && {
95
+ applicantLocationRequirements: applicantLocationRequirements.map(req => ({
96
+ '@type': req.type,
97
+ name: req.name,
98
+ })),
99
+ }),
100
+ ...(baseSalary && {
101
+ baseSalary: {
102
+ '@type': 'MonetaryAmount',
103
+ currency: baseSalary.currency,
104
+ value: typeof baseSalary.value === 'number'
105
+ ? { '@type': 'QuantitativeValue', value: baseSalary.value, unitText: baseSalary.unitText }
106
+ : {
107
+ '@type': 'QuantitativeValue',
108
+ minValue: baseSalary.value.minValue,
109
+ maxValue: baseSalary.value.maxValue,
110
+ unitText: baseSalary.unitText,
111
+ },
112
+ },
113
+ }),
114
+ ...(identifier && {
115
+ identifier: {
116
+ '@type': 'PropertyValue',
117
+ name: identifier.name,
118
+ value: identifier.value,
119
+ },
120
+ }),
121
+ ...(directApply !== undefined && { directApply }),
122
+ ...(experienceRequirements && { experienceRequirements }),
123
+ ...(educationRequirements && {
124
+ educationRequirements: {
125
+ '@type': 'EducationalOccupationalCredential',
126
+ credentialCategory: educationRequirements,
127
+ },
128
+ }),
129
+ ...(skills && { skills }),
130
+ ...(qualifications && { qualifications }),
131
+ ...(responsibilities && { responsibilities }),
132
+ ...(industry && { industry }),
133
+ ...(occupationalCategory && { occupationalCategory }),
134
+ };
135
+ ---
136
+
137
+ <script is:inline type="application/ld+json" set:html={JSON.stringify(jsonLd)} />
138
+ <slot />
@@ -0,0 +1,155 @@
1
+ ---
2
+ export interface OpeningHoursSpecification {
3
+ dayOfWeek: ('Monday' | 'Tuesday' | 'Wednesday' | 'Thursday' | 'Friday' | 'Saturday' | 'Sunday')[];
4
+ opens: string;
5
+ closes: string;
6
+ validFrom?: string;
7
+ validThrough?: string;
8
+ }
9
+
10
+ export interface Props {
11
+ type?: 'LocalBusiness' | 'Restaurant' | 'Store' | 'MedicalBusiness' | 'FinancialService' | 'FoodEstablishment' | 'HealthAndBeautyBusiness' | 'HomeAndConstructionBusiness' | 'LegalService' | 'LodgingBusiness' | 'ProfessionalService' | 'SportsActivityLocation';
12
+ name: string;
13
+ description?: string;
14
+ url?: string;
15
+ telephone?: string;
16
+ email?: string;
17
+ image?: string | string[];
18
+ priceRange?: string;
19
+ address: {
20
+ streetAddress: string;
21
+ addressLocality: string;
22
+ addressRegion?: string;
23
+ postalCode: string;
24
+ addressCountry: string;
25
+ };
26
+ geo?: {
27
+ latitude: number;
28
+ longitude: number;
29
+ };
30
+ openingHoursSpecification?: OpeningHoursSpecification[];
31
+ aggregateRating?: {
32
+ ratingValue: number;
33
+ reviewCount?: number;
34
+ ratingCount?: number;
35
+ bestRating?: number;
36
+ worstRating?: number;
37
+ };
38
+ review?: {
39
+ author: string;
40
+ datePublished: string;
41
+ reviewBody: string;
42
+ reviewRating?: {
43
+ ratingValue: number;
44
+ bestRating?: number;
45
+ worstRating?: number;
46
+ };
47
+ }[];
48
+ servesCuisine?: string[];
49
+ menu?: string;
50
+ acceptsReservations?: boolean;
51
+ hasMenu?: string;
52
+ paymentAccepted?: string[];
53
+ currenciesAccepted?: string;
54
+ areaServed?: string[];
55
+ sameAs?: string[];
56
+ }
57
+
58
+ const {
59
+ type = 'LocalBusiness',
60
+ name,
61
+ description,
62
+ url,
63
+ telephone,
64
+ email,
65
+ image,
66
+ priceRange,
67
+ address,
68
+ geo,
69
+ openingHoursSpecification,
70
+ aggregateRating,
71
+ review,
72
+ servesCuisine,
73
+ menu,
74
+ acceptsReservations,
75
+ hasMenu,
76
+ paymentAccepted,
77
+ currenciesAccepted,
78
+ areaServed,
79
+ sameAs,
80
+ } = Astro.props;
81
+
82
+ const jsonLd = {
83
+ '@context': 'https://schema.org',
84
+ '@type': type,
85
+ name,
86
+ ...(description && { description }),
87
+ ...(url && { url }),
88
+ ...(telephone && { telephone }),
89
+ ...(email && { email }),
90
+ ...(image && { image: Array.isArray(image) ? image : [image] }),
91
+ ...(priceRange && { priceRange }),
92
+ address: {
93
+ '@type': 'PostalAddress',
94
+ streetAddress: address.streetAddress,
95
+ addressLocality: address.addressLocality,
96
+ ...(address.addressRegion && { addressRegion: address.addressRegion }),
97
+ postalCode: address.postalCode,
98
+ addressCountry: address.addressCountry,
99
+ },
100
+ ...(geo?.latitude != null && geo?.longitude != null && {
101
+ geo: {
102
+ '@type': 'GeoCoordinates',
103
+ latitude: geo.latitude,
104
+ longitude: geo.longitude,
105
+ },
106
+ }),
107
+ ...(openingHoursSpecification && {
108
+ openingHoursSpecification: openingHoursSpecification.map(spec => ({
109
+ '@type': 'OpeningHoursSpecification',
110
+ dayOfWeek: spec.dayOfWeek,
111
+ opens: spec.opens,
112
+ closes: spec.closes,
113
+ ...(spec.validFrom && { validFrom: spec.validFrom }),
114
+ ...(spec.validThrough && { validThrough: spec.validThrough }),
115
+ })),
116
+ }),
117
+ ...(aggregateRating && {
118
+ aggregateRating: {
119
+ '@type': 'AggregateRating',
120
+ ratingValue: aggregateRating.ratingValue,
121
+ ...(aggregateRating.reviewCount && { reviewCount: aggregateRating.reviewCount }),
122
+ ...(aggregateRating.ratingCount && { ratingCount: aggregateRating.ratingCount }),
123
+ bestRating: aggregateRating.bestRating ?? 5,
124
+ worstRating: aggregateRating.worstRating ?? 1,
125
+ },
126
+ }),
127
+ ...(review && {
128
+ review: review.map(r => ({
129
+ '@type': 'Review',
130
+ author: { '@type': 'Person', name: r.author },
131
+ datePublished: r.datePublished,
132
+ reviewBody: r.reviewBody,
133
+ ...(r.reviewRating && {
134
+ reviewRating: {
135
+ '@type': 'Rating',
136
+ ratingValue: r.reviewRating.ratingValue,
137
+ bestRating: r.reviewRating.bestRating ?? 5,
138
+ worstRating: r.reviewRating.worstRating ?? 1,
139
+ },
140
+ }),
141
+ })),
142
+ }),
143
+ ...(servesCuisine && { servesCuisine }),
144
+ ...(menu && { menu }),
145
+ ...(acceptsReservations !== undefined && { acceptsReservations }),
146
+ ...(hasMenu && { hasMenu }),
147
+ ...(paymentAccepted && { paymentAccepted }),
148
+ ...(currenciesAccepted && { currenciesAccepted }),
149
+ ...(areaServed && { areaServed }),
150
+ ...(sameAs && { sameAs }),
151
+ };
152
+ ---
153
+
154
+ <script is:inline type="application/ld+json" set:html={JSON.stringify(jsonLd)} />
155
+ <slot />
@@ -0,0 +1,54 @@
1
+ ---
2
+ export interface Props {
3
+ name: string;
4
+ url: string;
5
+ description?: string;
6
+ potentialAction: {
7
+ target: string;
8
+ mathExpression?: string;
9
+ eduQuestionType?: ('Polynomial' | 'Arithmetic' | 'Calculus' | 'Geometry' | 'Statistics' | 'Algebra' | 'Trigonometry')[];
10
+ };
11
+ applicationCategory?: string;
12
+ operatingSystem?: string;
13
+ offers?: {
14
+ price: number | string;
15
+ priceCurrency: string;
16
+ };
17
+ }
18
+
19
+ const {
20
+ name,
21
+ url,
22
+ description,
23
+ potentialAction,
24
+ applicationCategory,
25
+ operatingSystem,
26
+ offers,
27
+ } = Astro.props;
28
+
29
+ const jsonLd = {
30
+ '@context': 'https://schema.org',
31
+ '@type': 'WebApplication',
32
+ name,
33
+ url,
34
+ ...(description && { description }),
35
+ ...(applicationCategory && { applicationCategory }),
36
+ ...(operatingSystem && { operatingSystem }),
37
+ ...(offers && {
38
+ offers: {
39
+ '@type': 'Offer',
40
+ price: offers.price,
41
+ priceCurrency: offers.priceCurrency,
42
+ },
43
+ }),
44
+ potentialAction: {
45
+ '@type': 'SolveMathAction',
46
+ target: potentialAction.target,
47
+ ...(potentialAction.mathExpression && { 'mathExpression-input': `required name=${potentialAction.mathExpression}` }),
48
+ ...(potentialAction.eduQuestionType && { eduQuestionType: potentialAction.eduQuestionType }),
49
+ },
50
+ };
51
+ ---
52
+
53
+ <script is:inline type="application/ld+json" set:html={JSON.stringify(jsonLd)} />
54
+ <slot />