Skip to content

Commit 09d442c

Browse files
committed
feat: fixes
1 parent 9e0c0aa commit 09d442c

5 files changed

Lines changed: 98 additions & 72 deletions

File tree

next-sitemap.config.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,14 @@
22
module.exports = {
33
siteUrl: process.env.SITE_URL || "https://workplacify.com",
44
generateRobotsTxt: true,
5-
exclude: ["/app*", "/legal", "/privacy-policy", "/terms-of-use", "/signin"],
5+
exclude: [
6+
"/app*",
7+
"/legal",
8+
"/privacy-policy",
9+
"/terms-of-use",
10+
"/signin",
11+
// Exclude translated pages – only canonical (default locale) URLs belong in the sitemap
12+
"/de*",
13+
"/it*",
14+
],
615
};

src/blogs/blogArticles.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export const blogArticles: BlogArticle[] = [
55
uuid: "2b3c4d5e-6f7g-8h9i-0j1k-l2m3n4o5p6q7",
66
title: "Desk Booking for Law Firms: Balancing Privacy & Productivity",
77
description:
8-
"Discover how desk booking systems help law firms adopt flexible seating while protecting client confidentiality and boosting productivity. Address the unique needs of law firms, such as the need for quiet spaces and confidentiality, and how a booking system can help.",
8+
"How desk booking helps law firms adopt flexible seating while protecting client confidentiality and boosting productivity.",
99
readingTime: "10 min",
1010
url: "/blog/desk-booking-law-firms-privacy-productivity",
1111
image: "/og-images/desk-booking-law-firms-privacy-productivity.png",
@@ -26,7 +26,7 @@ export const blogArticles: BlogArticle[] = [
2626
uuid: "1a2b3c4d-5e6f-7081-92a3-b4c5d6e7f809",
2727
title: "Workplacify Analytics: Turn Desk Booking Data into ROI",
2828
description:
29-
"Introducing Workplacify Analytics. Learn how Base and People Analytics convert desk booking data into real estate savings, productivity gains, and better hybrid experiences.",
29+
"Workplacify Analytics turns desk booking data into ROI. Base and People Analytics drive real estate savings and better hybrid experiences.",
3030
readingTime: "3 min",
3131
url: "/blog/introducing-desk-booking-analytics",
3232
image: "/og-images/introducing-desk-booking-analytics.png",
@@ -47,7 +47,7 @@ export const blogArticles: BlogArticle[] = [
4747
uuid: "f8e7d6c5-b4a3-9201-8765-432109876543",
4848
title: "Create a Desk Sharing Policy That Works",
4949
description:
50-
"Learn how to create and implement a comprehensive desk sharing policy that your employees will embrace. Get step-by-step guidance on building a fair, efficient hot desking system with the right tools and communication strategy.",
50+
"Create a desk sharing policy that works. Step-by-step guide to building a fair, efficient hot desking system your team will embrace.",
5151
readingTime: "12 min",
5252
url: "/blog/working-desk-sharing-policy",
5353
image: "/og-images/working-desk-sharing-policy.png",
@@ -77,7 +77,7 @@ export const blogArticles: BlogArticle[] = [
7777
uuid: "a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6",
7878
title: "Master Your Office: Desk Booking Limitations with Workplacify",
7979
description:
80-
"Learn how to optimize your hybrid workspace with smart desk booking limitations. Discover how Workplacify's new Office Settings feature helps prevent ghost bookings, ensures fair access, and delivers real ROI through better space utilization.",
80+
"Optimize hybrid workspaces with smart desk booking limits. Prevent ghost bookings, ensure fair access, and get real ROI from better space use.",
8181
readingTime: "8 min",
8282
url: "/blog/desk-booking-limitations",
8383
image: "/og-images/desk-booking-limitations.png",
@@ -107,7 +107,7 @@ export const blogArticles: BlogArticle[] = [
107107
uuid: "d4e5f6g7-h8i9-j0k1-l2m3-n4o5p6q7r8s9",
108108
title: "Why We Chose to Be Open-Source: The Workplacify Philosophy",
109109
description:
110-
"Discover why we built Workplacify as an open-source platform. Learn how transparency, community collaboration, and freedom to adapt are reshaping workplace technology.",
110+
"Why we built Workplacify as open-source: transparency, community collaboration, and freedom to adapt reshape workplace technology.",
111111
readingTime: "7 min",
112112
url: "/blog/open-source-desk-booking-philosophy",
113113
image: "/og-images/open-source-desk-booking-philosophy.png",
@@ -137,7 +137,7 @@ export const blogArticles: BlogArticle[] = [
137137
uuid: "b1c2d3e4-f5g6-7h8i-9j0k-l1m2n3o4p5q7",
138138
title: "Empty Office? How to Improve Office Occupancy",
139139
description:
140-
"Learn practical strategies to improve office occupancy in your startup. From data-driven insights to creating a magnetic workplace, discover how to make your office a place where people want to be.",
140+
"Practical strategies to improve office occupancy. Data-driven insights to create a workplace where people want to be.",
141141
readingTime: "15 min",
142142
url: "/blog/empty-office-improve-occupancy",
143143
image: "/og-images/empty-office-improve-occupancy.png",
@@ -167,7 +167,7 @@ export const blogArticles: BlogArticle[] = [
167167
uuid: "e7b2c3d4-f5g6-7h8i-9j0k-l1m2n3o4p5q6",
168168
title: "5 Hot Desking Problems & How to Solve Them",
169169
description:
170-
"Discover practical solutions to common hot desking challenges, from desk hunting to team culture. Learn how to create an efficient, collaborative flexible workspace.",
170+
"Practical solutions to hot desking challenges: desk hunting, team culture. Build an efficient, collaborative flexible workspace.",
171171
readingTime: "12 min",
172172
url: "/blog/solve-hot-desking-problems",
173173
image: "/og-images/five-hot-desking-problems-how-to-solve-them.png",
@@ -287,7 +287,7 @@ export const blogArticles: BlogArticle[] = [
287287
uuid: "cbc011d8-ba37-4aea-a126-135f87ba36ba",
288288
title: "Desk Sharing for Startups: Cut Costs & Boost Agility",
289289
description:
290-
"Learn how desk sharing helps startups cut costs, boost collaboration, and attract top talent. Discover the financial and cultural benefits of flexible seating.",
290+
"Desk sharing for startups: cut costs, boost collaboration, attract top talent. Financial and cultural benefits of flexible seating.",
291291
readingTime: "8 min",
292292
url: "/blog/desk-sharing-startups-cut-costs-boost-agility",
293293
image: "/og-images/desk-sharing-startups-cut-costs-boost-agility.png",
@@ -317,7 +317,7 @@ export const blogArticles: BlogArticle[] = [
317317
uuid: "9ee34256-8c5c-4428-b5c8-764d74167a97",
318318
title: "5 Signs You've Outgrown Your Office Spreadsheet",
319319
description:
320-
"Discover 5 signs you've outgrown your office spreadsheet, from errors to security risks, and learn when to upgrade to smarter workplace management tools.",
320+
"5 signs you've outgrown your office spreadsheet. Discover when to upgrade to smarter workplace management tools.",
321321
readingTime: "8 min",
322322
url: "/blog/signs-outgrown-office-spreadsheet",
323323
image: "/og-images/signs-outgrown-office-spreadsheet.png",
@@ -347,7 +347,7 @@ export const blogArticles: BlogArticle[] = [
347347
uuid: "a08a4a54-a5e6-4a6b-a915-8ff273d465ba",
348348
title: "Desk Scheduling: The Hidden Costs of Your Spreadsheet",
349349
description:
350-
"Tired of your desk scheduling spreadsheet? Uncover the hidden costs in wasted admin time, lost productivity, and poor data. Learn how modern office management software can boost workspace efficiency and provide the analytics you need to make smarter decisions.",
350+
"Uncover the hidden costs of desk scheduling spreadsheets: wasted admin time, lost productivity. Learn how modern software boosts efficiency.",
351351
readingTime: "8 min",
352352
url: "/blog/desk-scheduling-the-hidden-costs-of-your-spreadsheet",
353353
image:

src/pages/blog/desk-sharing-startups-cut-costs-boost-agility/index.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -698,8 +698,7 @@ const BlogContent = () => {
698698
const DeskSharingStartupsCutCostsBoostAgility = () => {
699699
const url = `${process.env.NEXT_PUBLIC_BASE_URL}/blog/desk-sharing-startups-cut-costs-boost-agility`;
700700
const title = "Desk Sharing for Startups: Cut Costs & Boost Agility";
701-
const description =
702-
"Learn how desk sharing can help startups reduce real estate costs by 30%, improve team collaboration, and attract top talent. A strategic guide to flexible workspace management.";
701+
const description = article.description;
703702

704703
return (
705704
<>

src/pages/blog/signs-outgrown-office-spreadsheet/index.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -682,14 +682,13 @@ const BlogArticleComponent = () => {
682682
<>
683683
<NextSeo
684684
title="5 Signs You've Outgrown Your Office Spreadsheet"
685-
description="Is your office spreadsheet becoming a bottleneck? Discover the 5 telltale signs that indicate it's time to upgrade your workplace management tools, and learn how to transition to a more efficient solution."
685+
description={article.description}
686686
canonical={url}
687687
openGraph={{
688688
url,
689689
type: "article",
690690
title: "5 Signs You've Outgrown Your Office Spreadsheet",
691-
description:
692-
"Is your office spreadsheet becoming a bottleneck? Discover the 5 telltale signs that indicate it's time to upgrade your workplace management tools, and learn how to transition to a more efficient solution.",
691+
description: article.description,
693692
images: [
694693
{
695694
url: `${url}/og-images/5-signs-youve-outgrown-your-office-spreadsheet.png`,

src/pages/office-utilization/[location]/index.tsx

Lines changed: 75 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,14 @@ const OfficeUtilizationPage = (
7474
);
7575

7676
const t = useTranslations("OfficeUtilization");
77-
const { locationData, comparisonCities: comparisonCitiesFromProps } = props;
77+
const {
78+
locationData,
79+
comparisonCities: comparisonCitiesFromProps,
80+
relatedCities: relatedCitiesFromProps,
81+
} = props;
7882
const comparisonCities = comparisonCitiesFromProps as LocationData[];
83+
const relatedCities = (relatedCitiesFromProps ??
84+
comparisonCities) as LocationData[];
7985
let [sortedBlogArticles] = createShuffle([
8086
sortedBlogArticlesButNotShuffled,
8187
locationData.city,
@@ -473,7 +479,7 @@ const OfficeUtilizationPage = (
473479
flexWrap="wrap"
474480
align={{ base: "flex-start", md: "center" }}
475481
>
476-
{comparisonCities.flatMap((city, index) => {
482+
{relatedCities.flatMap((city, index) => {
477483
const items = [
478484
<Box key={city.city}>
479485
<Link colorPalette="orange" asChild>
@@ -485,7 +491,7 @@ const OfficeUtilizationPage = (
485491
</Link>
486492
</Box>,
487493
];
488-
if (index < comparisonCities.length - 1) {
494+
if (index < relatedCities.length - 1) {
489495
items.push(
490496
<Box
491497
key={`separator-${index}`}
@@ -614,84 +620,97 @@ export const getStaticProps: GetStaticProps = async (context) => {
614620
return { notFound: true, props: { messages } };
615621
}
616622

617-
const comparisonCities = records.filter((record) => {
618-
return Boolean(
619-
record.country === locationData.country &&
620-
record.region === locationData.region &&
621-
record.city !== locationData.city,
622-
);
623-
});
623+
// Same-region cities for chart comparison (wider than same-country for single-city countries)
624+
const sameRegionCities = records.filter(
625+
(record) =>
626+
record.region === locationData.region &&
627+
record.city !== locationData.city,
628+
);
624629

625630
const finalComparisonCities: LocationData[] = [];
626631

627-
const sortedAttendanceSameRegion = comparisonCities
628-
.sort((a, b) => {
629-
return (
630-
parseInt(a.attendance_high_percentage) -
631-
parseInt(b.attendance_high_percentage)
632-
);
633-
})
634-
.slice(0, 2);
632+
// Prefer same-country peers for chart relevance, then same-region
633+
const sameCountryCities = sameRegionCities.filter(
634+
(record) => record.country === locationData.country,
635+
);
636+
const comparisonPool =
637+
sameCountryCities.length >= 4 ? sameCountryCities : sameRegionCities;
635638

636-
finalComparisonCities.push(...sortedAttendanceSameRegion);
639+
const sortedAttendance = [...comparisonPool].sort(
640+
(a, b) =>
641+
parseInt(a.attendance_high_percentage) -
642+
parseInt(b.attendance_high_percentage),
643+
);
644+
finalComparisonCities.push(...sortedAttendance.slice(0, 2));
637645

638-
const sortedByEstimatedSavings = comparisonCities
646+
const sortedByLowestSavings = comparisonPool
639647
.filter(
640648
(record) => !finalComparisonCities.some((r) => r.city === record.city),
641649
)
642-
.sort((a, b) => {
643-
return (
650+
.sort(
651+
(a, b) =>
644652
parseInt(a.est_savings_usd_per_emp) -
645-
parseInt(b.est_savings_usd_per_emp)
646-
);
647-
})
648-
.slice(0, 2);
649-
650-
finalComparisonCities.push(...sortedByEstimatedSavings);
653+
parseInt(b.est_savings_usd_per_emp),
654+
);
655+
finalComparisonCities.push(...sortedByLowestSavings.slice(0, 2));
651656

652-
const descendingEstimatedSavings = comparisonCities
657+
const sortedByHighestSavings = comparisonPool
653658
.filter(
654659
(record) => !finalComparisonCities.some((r) => r.city === record.city),
655660
)
656-
.sort((a, b) => {
657-
return (
658-
parseInt(a.est_savings_usd_per_emp) -
659-
parseInt(b.est_savings_usd_per_emp)
660-
);
661-
})
662-
.slice(0, 2);
663-
finalComparisonCities.push(...descendingEstimatedSavings);
661+
.sort(
662+
(a, b) =>
663+
parseInt(b.est_savings_usd_per_emp) -
664+
parseInt(a.est_savings_usd_per_emp),
665+
);
666+
finalComparisonCities.push(...sortedByHighestSavings.slice(0, 2));
664667

665-
const bigCitiesList = [
668+
// Fill remaining slots with hub cities (deterministic per city via seeded shuffle)
669+
const hubCitySlugs = [
666670
"london",
667671
"new-york",
668672
"hong-kong",
669673
"tokyo",
670674
"shanghai",
675+
"singapore",
676+
"dubai",
677+
"paris",
678+
"sydney",
679+
"berlin",
671680
];
672-
// Shuffle big cities as well
673-
const bigCities = records
674-
.filter(
675-
(record) =>
676-
kebabCase(record.city.toLowerCase()) !==
677-
kebabCase(locationData.city.toLowerCase()) &&
678-
bigCitiesList.includes(kebabCase(record.city.toLowerCase())),
679-
)
680-
.sort(() => {
681-
return Math.random() - 0.5;
682-
});
681+
const hubCities = records.filter(
682+
(record) =>
683+
kebabCase(record.city.toLowerCase()) !==
684+
kebabCase(locationData.city.toLowerCase()) &&
685+
hubCitySlugs.includes(kebabCase(record.city.toLowerCase())),
686+
);
683687

684-
while (finalComparisonCities.length < 8 && bigCities.length > 0) {
685-
if (finalComparisonCities.length < 8) {
686-
const randomCity = bigCities.shift();
687-
if (randomCity) {
688-
finalComparisonCities.push(randomCity);
689-
}
688+
const citySeed = [...locationData.city].reduce(
689+
(h, c) => (h * 31 + c.charCodeAt(0)) | 0,
690+
0,
691+
);
692+
const [shuffledHubCities] = createShuffle([hubCities, citySeed]);
693+
for (const city of shuffledHubCities) {
694+
if (finalComparisonCities.length >= 8) break;
695+
if (!finalComparisonCities.some((r) => r.city === city.city)) {
696+
finalComparisonCities.push(city);
690697
}
691698
}
692699

700+
// Related cities for internal links: wider pool, seeded shuffle for diverse link distribution
701+
const allOtherCities = records.filter(
702+
(record) => record.city !== locationData.city,
703+
);
704+
const [shuffledRelated] = createShuffle([allOtherCities, citySeed]);
705+
const relatedCities = shuffledRelated.slice(0, 20);
706+
693707
return {
694-
props: { messages, locationData, comparisonCities: finalComparisonCities },
708+
props: {
709+
messages,
710+
locationData,
711+
comparisonCities: finalComparisonCities,
712+
relatedCities,
713+
},
695714
};
696715
};
697716

0 commit comments

Comments
 (0)