@scalemule/news-publication 0.0.8 → 0.0.9

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.
@@ -13,6 +13,15 @@ var CLASSIFIED_CATEGORIES = [
13
13
  { value: "community", label: "Community" },
14
14
  { value: "wanted", label: "Wanted" }
15
15
  ];
16
+ function dealsFeedPath() {
17
+ return "/v1/deals/feed";
18
+ }
19
+ function dealGoPath(offerId) {
20
+ return `/v1/deals/go/${encodeURIComponent(offerId)}`;
21
+ }
22
+ function dealDollars(cents) {
23
+ return cents != null ? `$${(cents / 100).toFixed(2)}` : null;
24
+ }
16
25
  function clipChars(value, max) {
17
26
  const chars = Array.from(value.trim());
18
27
  return chars.length <= max ? chars.join("") : chars.slice(0, max).join("");
@@ -109,4 +118,4 @@ function prepareClassified(input, towns, region, now = /* @__PURE__ */ new Date(
109
118
  };
110
119
  }
111
120
 
112
- export { CLASSIFIED_CATEGORIES, JOB_EMPLOYMENT_TYPES, categoryLabel, classifiedPath, classifiedSearchPath, clipChars, employmentLabel, isClassifiedCategory, isEmploymentType, jobEmployerBody, jobOpeningBody, jobPostingBody, prepareClassified, prepareJobPost, publicJobPath, publicJobsPath };
121
+ export { CLASSIFIED_CATEGORIES, JOB_EMPLOYMENT_TYPES, categoryLabel, classifiedPath, classifiedSearchPath, clipChars, dealDollars, dealGoPath, dealsFeedPath, employmentLabel, isClassifiedCategory, isEmploymentType, jobEmployerBody, jobOpeningBody, jobPostingBody, prepareClassified, prepareJobPost, publicJobPath, publicJobsPath };
package/dist/index.cjs CHANGED
@@ -590,6 +590,15 @@ var CLASSIFIED_CATEGORIES = [
590
590
  { value: "community", label: "Community" },
591
591
  { value: "wanted", label: "Wanted" }
592
592
  ];
593
+ function dealsFeedPath() {
594
+ return "/v1/deals/feed";
595
+ }
596
+ function dealGoPath(offerId) {
597
+ return `/v1/deals/go/${encodeURIComponent(offerId)}`;
598
+ }
599
+ function dealDollars(cents) {
600
+ return cents != null ? `$${(cents / 100).toFixed(2)}` : null;
601
+ }
593
602
  function clipChars(value, max) {
594
603
  const chars = Array.from(value.trim());
595
604
  return chars.length <= max ? chars.join("") : chars.slice(0, max).join("");
@@ -1398,6 +1407,9 @@ exports.classifiedSearchPath = classifiedSearchPath;
1398
1407
  exports.clearCookieConsent = clearCookieConsent;
1399
1408
  exports.clipChars = clipChars;
1400
1409
  exports.composeEdition = composeEdition;
1410
+ exports.dealDollars = dealDollars;
1411
+ exports.dealGoPath = dealGoPath;
1412
+ exports.dealsFeedPath = dealsFeedPath;
1401
1413
  exports.defaultRevalidateTargets = defaultRevalidateTargets;
1402
1414
  exports.definePublication = definePublication;
1403
1415
  exports.displayHeadline = displayHeadline;
package/dist/index.d.cts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { P as PublicationTheme, a as PublicationInput, N as NewsPublication, F as FeatureName, H as HomepageSelection, b as NearbyUpdate, c as NewsItem, E as EditorialSection, d as NewsArticle, C as CouponOffer } from './snapshot-BS4hn1dk.cjs';
2
2
  export { A as ArticleCitation, e as CategoryRef, f as CouponSettings, g as CoverImageMetadata, h as FEATURE_NAMES, i as FeatureFlags, j as NewsFeed, k as NewsLayoutId, l as PublicationModules, m as PublicationRecord, n as PublicationSnapshot, R as ReferralSettings, S as SnapshotHooks, o as StoryDisplay, p as SyncSource, q as SyncedPublication, T as TagRef, r as acceptRemoteRecord, s as acceptSnapshot, t as parseSnapshot, u as publicationFromSnapshot, v as publicationRecord, w as snapshotFromRecord, x as syncPublication } from './snapshot-BS4hn1dk.cjs';
3
3
  export { M as MEET_ACTIVITIES, a as MeetActivity, b as MeetupInput, P as PreparedMeetup, c as PublicPlace, d as activityLabel, e as canHostMeetup, i as isAdult, p as prepareMeetup, s as screenMessage } from './meet-5Ym5bxfh.cjs';
4
- export { C as CLASSIFIED_CATEGORIES, a as ClassifiedCategory, b as ClassifiedListing, E as EmploymentType, J as JOB_EMPLOYMENT_TYPES, P as PreparedClassified, c as PreparedJob, d as PublicJob, e as categoryLabel, f as classifiedPath, g as classifiedSearchPath, h as clipChars, i as employmentLabel, j as isClassifiedCategory, k as isEmploymentType, l as jobEmployerBody, m as jobOpeningBody, n as jobPostingBody, p as prepareClassified, o as prepareJobPost, q as publicJobPath, r as publicJobsPath } from './marketplace-Ch7mdJZv.cjs';
4
+ export { C as CLASSIFIED_CATEGORIES, a as ClassifiedCategory, b as ClassifiedListing, E as EmploymentType, J as JOB_EMPLOYMENT_TYPES, P as PreparedClassified, c as PreparedJob, d as PublicDeal, e as PublicDealOffer, f as PublicJob, g as categoryLabel, h as classifiedPath, i as classifiedSearchPath, j as clipChars, k as dealDollars, l as dealGoPath, m as dealsFeedPath, n as employmentLabel, o as isClassifiedCategory, p as isEmploymentType, q as jobEmployerBody, r as jobOpeningBody, s as jobPostingBody, t as prepareClassified, u as prepareJobPost, v as publicJobPath, w as publicJobsPath } from './marketplace-CpGWPbFk.cjs';
5
5
  export { CookieConsentBanner, CookieConsentBannerProps } from './cookie-consent-banner.cjs';
6
6
  export { CookieSettingsView, CookieSettingsViewProps } from './cookie-settings-view.cjs';
7
7
  import 'react';
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { P as PublicationTheme, a as PublicationInput, N as NewsPublication, F as FeatureName, H as HomepageSelection, b as NearbyUpdate, c as NewsItem, E as EditorialSection, d as NewsArticle, C as CouponOffer } from './snapshot-BS4hn1dk.js';
2
2
  export { A as ArticleCitation, e as CategoryRef, f as CouponSettings, g as CoverImageMetadata, h as FEATURE_NAMES, i as FeatureFlags, j as NewsFeed, k as NewsLayoutId, l as PublicationModules, m as PublicationRecord, n as PublicationSnapshot, R as ReferralSettings, S as SnapshotHooks, o as StoryDisplay, p as SyncSource, q as SyncedPublication, T as TagRef, r as acceptRemoteRecord, s as acceptSnapshot, t as parseSnapshot, u as publicationFromSnapshot, v as publicationRecord, w as snapshotFromRecord, x as syncPublication } from './snapshot-BS4hn1dk.js';
3
3
  export { M as MEET_ACTIVITIES, a as MeetActivity, b as MeetupInput, P as PreparedMeetup, c as PublicPlace, d as activityLabel, e as canHostMeetup, i as isAdult, p as prepareMeetup, s as screenMessage } from './meet-5Ym5bxfh.js';
4
- export { C as CLASSIFIED_CATEGORIES, a as ClassifiedCategory, b as ClassifiedListing, E as EmploymentType, J as JOB_EMPLOYMENT_TYPES, P as PreparedClassified, c as PreparedJob, d as PublicJob, e as categoryLabel, f as classifiedPath, g as classifiedSearchPath, h as clipChars, i as employmentLabel, j as isClassifiedCategory, k as isEmploymentType, l as jobEmployerBody, m as jobOpeningBody, n as jobPostingBody, p as prepareClassified, o as prepareJobPost, q as publicJobPath, r as publicJobsPath } from './marketplace-Ch7mdJZv.js';
4
+ export { C as CLASSIFIED_CATEGORIES, a as ClassifiedCategory, b as ClassifiedListing, E as EmploymentType, J as JOB_EMPLOYMENT_TYPES, P as PreparedClassified, c as PreparedJob, d as PublicDeal, e as PublicDealOffer, f as PublicJob, g as categoryLabel, h as classifiedPath, i as classifiedSearchPath, j as clipChars, k as dealDollars, l as dealGoPath, m as dealsFeedPath, n as employmentLabel, o as isClassifiedCategory, p as isEmploymentType, q as jobEmployerBody, r as jobOpeningBody, s as jobPostingBody, t as prepareClassified, u as prepareJobPost, v as publicJobPath, w as publicJobsPath } from './marketplace-CpGWPbFk.js';
5
5
  export { CookieConsentBanner, CookieConsentBannerProps } from './cookie-consent-banner.js';
6
6
  export { CookieSettingsView, CookieSettingsViewProps } from './cookie-settings-view.js';
7
7
  import 'react';
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { definePublication } from './chunk-C7JTEHTH.js';
2
2
  export { BROADSHEET_THEME, FEATURE_NAMES, LAYOUT_COOKIE, acceptRemoteRecord, acceptSnapshot, definePublication, featureEnabled, parseSnapshot, publicationFromSnapshot, publicationRecord, resolveLayout, snapshotFromRecord, syncPublication } from './chunk-C7JTEHTH.js';
3
- export { CLASSIFIED_CATEGORIES, JOB_EMPLOYMENT_TYPES, categoryLabel, classifiedPath, classifiedSearchPath, clipChars, employmentLabel, isClassifiedCategory, isEmploymentType, jobEmployerBody, jobOpeningBody, jobPostingBody, prepareClassified, prepareJobPost, publicJobPath, publicJobsPath } from './chunk-YQXS2KQI.js';
3
+ export { CLASSIFIED_CATEGORIES, JOB_EMPLOYMENT_TYPES, categoryLabel, classifiedPath, classifiedSearchPath, clipChars, dealDollars, dealGoPath, dealsFeedPath, employmentLabel, isClassifiedCategory, isEmploymentType, jobEmployerBody, jobOpeningBody, jobPostingBody, prepareClassified, prepareJobPost, publicJobPath, publicJobsPath } from './chunk-C7ZPLH2K.js';
4
4
  export { MEET_ACTIVITIES, activityLabel, canHostMeetup, isAdult, prepareMeetup, screenMessage } from './chunk-GB36YM56.js';
5
5
  export { CookieConsentBanner } from './chunk-PYCU2T5O.js';
6
6
  export { CookieSettingsView } from './chunk-R33MJWND.js';
@@ -43,6 +43,25 @@ type PublicJob = {
43
43
  remote_allowed?: boolean;
44
44
  published_at?: string | null;
45
45
  };
46
+ type PublicDealOffer = {
47
+ id: string;
48
+ merchant: string;
49
+ price_cents?: number | null;
50
+ };
51
+ type PublicDeal = {
52
+ id: string;
53
+ title: string;
54
+ body: string;
55
+ price_cents?: number | null;
56
+ compare_at_cents?: number | null;
57
+ coupon_code?: string | null;
58
+ image_url?: string | null;
59
+ locality?: string | null;
60
+ offers: PublicDealOffer[];
61
+ };
62
+ declare function dealsFeedPath(): string;
63
+ declare function dealGoPath(offerId: string): string;
64
+ declare function dealDollars(cents?: number | null): string | null;
46
65
  type ClassifiedListing = {
47
66
  id: string;
48
67
  title: string;
@@ -120,4 +139,4 @@ declare function prepareClassified(input: {
120
139
  price?: unknown;
121
140
  }, towns: string[], region: string, now?: Date): PreparedClassified;
122
141
 
123
- export { CLASSIFIED_CATEGORIES as C, type EmploymentType as E, JOB_EMPLOYMENT_TYPES as J, type PreparedClassified as P, type ClassifiedCategory as a, type ClassifiedListing as b, type PreparedJob as c, type PublicJob as d, categoryLabel as e, classifiedPath as f, classifiedSearchPath as g, clipChars as h, employmentLabel as i, isClassifiedCategory as j, isEmploymentType as k, jobEmployerBody as l, jobOpeningBody as m, jobPostingBody as n, prepareJobPost as o, prepareClassified as p, publicJobPath as q, publicJobsPath as r };
142
+ export { CLASSIFIED_CATEGORIES as C, type EmploymentType as E, JOB_EMPLOYMENT_TYPES as J, type PreparedClassified as P, type ClassifiedCategory as a, type ClassifiedListing as b, type PreparedJob as c, type PublicDeal as d, type PublicDealOffer as e, type PublicJob as f, categoryLabel as g, classifiedPath as h, classifiedSearchPath as i, clipChars as j, dealDollars as k, dealGoPath as l, dealsFeedPath as m, employmentLabel as n, isClassifiedCategory as o, isEmploymentType as p, jobEmployerBody as q, jobOpeningBody as r, jobPostingBody as s, prepareClassified as t, prepareJobPost as u, publicJobPath as v, publicJobsPath as w };
@@ -43,6 +43,25 @@ type PublicJob = {
43
43
  remote_allowed?: boolean;
44
44
  published_at?: string | null;
45
45
  };
46
+ type PublicDealOffer = {
47
+ id: string;
48
+ merchant: string;
49
+ price_cents?: number | null;
50
+ };
51
+ type PublicDeal = {
52
+ id: string;
53
+ title: string;
54
+ body: string;
55
+ price_cents?: number | null;
56
+ compare_at_cents?: number | null;
57
+ coupon_code?: string | null;
58
+ image_url?: string | null;
59
+ locality?: string | null;
60
+ offers: PublicDealOffer[];
61
+ };
62
+ declare function dealsFeedPath(): string;
63
+ declare function dealGoPath(offerId: string): string;
64
+ declare function dealDollars(cents?: number | null): string | null;
46
65
  type ClassifiedListing = {
47
66
  id: string;
48
67
  title: string;
@@ -120,4 +139,4 @@ declare function prepareClassified(input: {
120
139
  price?: unknown;
121
140
  }, towns: string[], region: string, now?: Date): PreparedClassified;
122
141
 
123
- export { CLASSIFIED_CATEGORIES as C, type EmploymentType as E, JOB_EMPLOYMENT_TYPES as J, type PreparedClassified as P, type ClassifiedCategory as a, type ClassifiedListing as b, type PreparedJob as c, type PublicJob as d, categoryLabel as e, classifiedPath as f, classifiedSearchPath as g, clipChars as h, employmentLabel as i, isClassifiedCategory as j, isEmploymentType as k, jobEmployerBody as l, jobOpeningBody as m, jobPostingBody as n, prepareJobPost as o, prepareClassified as p, publicJobPath as q, publicJobsPath as r };
142
+ export { CLASSIFIED_CATEGORIES as C, type EmploymentType as E, JOB_EMPLOYMENT_TYPES as J, type PreparedClassified as P, type ClassifiedCategory as a, type ClassifiedListing as b, type PreparedJob as c, type PublicDeal as d, type PublicDealOffer as e, type PublicJob as f, categoryLabel as g, classifiedPath as h, classifiedSearchPath as i, clipChars as j, dealDollars as k, dealGoPath as l, dealsFeedPath as m, employmentLabel as n, isClassifiedCategory as o, isEmploymentType as p, jobEmployerBody as q, jobOpeningBody as r, jobPostingBody as s, prepareClassified as t, prepareJobPost as u, publicJobPath as v, publicJobsPath as w };
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { JOB_EMPLOYMENT_TYPES, CLASSIFIED_CATEGORIES } from './chunk-YQXS2KQI.js';
2
+ import { JOB_EMPLOYMENT_TYPES, CLASSIFIED_CATEGORIES } from './chunk-C7ZPLH2K.js';
3
3
  import { useState } from 'react';
4
4
  import { jsx, jsxs } from 'react/jsx-runtime';
5
5
 
@@ -17,6 +17,9 @@ var CLASSIFIED_CATEGORIES = [
17
17
  { value: "community", label: "Community" },
18
18
  { value: "wanted", label: "Wanted" }
19
19
  ];
20
+ function dealDollars(cents) {
21
+ return cents != null ? `$${(cents / 100).toFixed(2)}` : null;
22
+ }
20
23
  function employmentLabel(value) {
21
24
  return JOB_EMPLOYMENT_TYPES.find((item) => item.value === value)?.label ?? "";
22
25
  }
@@ -62,11 +65,29 @@ function JobArticle({ job }) {
62
65
  ] })
63
66
  ] });
64
67
  }
68
+ function DealsBlock({ deals }) {
69
+ if (deals.length === 0) return null;
70
+ return /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "deals", "aria-label": "Deals", children: [
71
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { children: "Deals" }),
72
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "meta", children: "We may earn a commission when you use these links." }),
73
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "list", children: deals.map((deal) => {
74
+ const offer = deal.offers[0];
75
+ if (!offer) return null;
76
+ const stores = deal.offers.map((item) => item.merchant).filter(Boolean).join(", ");
77
+ return /* @__PURE__ */ jsxRuntime.jsxs("a", { className: "item", href: `/classifieds/out/${encodeURIComponent(offer.id)}`, children: [
78
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { children: deal.title }),
79
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "meta", children: [stores, dealDollars(deal.price_cents ?? offer.price_cents), deal.coupon_code ? `Code ${deal.coupon_code}` : null, deal.locality].filter(Boolean).join(" \xB7 ") })
80
+ ] }, deal.id);
81
+ }) })
82
+ ] });
83
+ }
65
84
  function ClassifiedsBoard({
66
85
  region,
67
86
  listings,
68
- category
87
+ category,
88
+ deals = []
69
89
  }) {
90
+ const dealsFirst = listings.length < 8;
70
91
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "np-market", children: [
71
92
  /* @__PURE__ */ jsxRuntime.jsx("h1", { children: "Classifieds" }),
72
93
  /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "lead", children: [
@@ -82,13 +103,19 @@ function ClassifiedsBoard({
82
103
  /* @__PURE__ */ jsxRuntime.jsx("a", { className: !category ? "on" : void 0, href: "/classifieds", children: "All" }),
83
104
  CLASSIFIED_CATEGORIES.map((item) => /* @__PURE__ */ jsxRuntime.jsx("a", { className: category === item.value ? "on" : void 0, href: `/classifieds?category=${item.value}`, children: item.label }, item.value))
84
105
  ] }),
85
- listings.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "empty", children: [
106
+ dealsFirst ? /* @__PURE__ */ jsxRuntime.jsx(DealsBlock, { deals }) : null,
107
+ listings.length === 0 ? deals.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "empty", children: [
86
108
  /* @__PURE__ */ jsxRuntime.jsx("h2", { children: "No classifieds yet." }),
87
109
  /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Post something neighbors might need." })
110
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "meta", children: [
111
+ "No neighbor listings yet. ",
112
+ /* @__PURE__ */ jsxRuntime.jsx("a", { href: "/classifieds/post", children: "Place a listing" }),
113
+ "."
88
114
  ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "list", children: listings.map((listing) => /* @__PURE__ */ jsxRuntime.jsxs("a", { className: "item", href: `/classifieds/${encodeURIComponent(listing.id)}`, children: [
89
115
  /* @__PURE__ */ jsxRuntime.jsx("h2", { children: listing.title }),
90
116
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "meta", children: [categoryLabel(listing.category), listing.location?.town, money(listing.price)].filter(Boolean).join(" \xB7 ") })
91
- ] }, listing.id)) })
117
+ ] }, listing.id)) }),
118
+ dealsFirst ? null : /* @__PURE__ */ jsxRuntime.jsx(DealsBlock, { deals })
92
119
  ] });
93
120
  }
94
121
  function ClassifiedArticle({ listing }) {
@@ -1,5 +1,5 @@
1
1
  import * as react from 'react';
2
- import { b as ClassifiedListing, d as PublicJob } from './marketplace-Ch7mdJZv.cjs';
2
+ import { b as ClassifiedListing, d as PublicDeal, f as PublicJob } from './marketplace-CpGWPbFk.cjs';
3
3
 
4
4
  declare function JobsBoard({ region, jobs, query }: {
5
5
  region: string;
@@ -9,10 +9,11 @@ declare function JobsBoard({ region, jobs, query }: {
9
9
  declare function JobArticle({ job }: {
10
10
  job: PublicJob;
11
11
  }): react.JSX.Element;
12
- declare function ClassifiedsBoard({ region, listings, category, }: {
12
+ declare function ClassifiedsBoard({ region, listings, category, deals, }: {
13
13
  region: string;
14
14
  listings: ClassifiedListing[];
15
15
  category: string;
16
+ deals?: PublicDeal[];
16
17
  }): react.JSX.Element;
17
18
  declare function ClassifiedArticle({ listing }: {
18
19
  listing: ClassifiedListing;
@@ -1,5 +1,5 @@
1
1
  import * as react from 'react';
2
- import { b as ClassifiedListing, d as PublicJob } from './marketplace-Ch7mdJZv.js';
2
+ import { b as ClassifiedListing, d as PublicDeal, f as PublicJob } from './marketplace-CpGWPbFk.js';
3
3
 
4
4
  declare function JobsBoard({ region, jobs, query }: {
5
5
  region: string;
@@ -9,10 +9,11 @@ declare function JobsBoard({ region, jobs, query }: {
9
9
  declare function JobArticle({ job }: {
10
10
  job: PublicJob;
11
11
  }): react.JSX.Element;
12
- declare function ClassifiedsBoard({ region, listings, category, }: {
12
+ declare function ClassifiedsBoard({ region, listings, category, deals, }: {
13
13
  region: string;
14
14
  listings: ClassifiedListing[];
15
15
  category: string;
16
+ deals?: PublicDeal[];
16
17
  }): react.JSX.Element;
17
18
  declare function ClassifiedArticle({ listing }: {
18
19
  listing: ClassifiedListing;
@@ -1,4 +1,4 @@
1
- import { employmentLabel, CLASSIFIED_CATEGORIES, categoryLabel } from './chunk-YQXS2KQI.js';
1
+ import { employmentLabel, CLASSIFIED_CATEGORIES, categoryLabel, dealDollars } from './chunk-C7ZPLH2K.js';
2
2
  import { jsxs, jsx } from 'react/jsx-runtime';
3
3
 
4
4
  function money(price) {
@@ -40,11 +40,29 @@ function JobArticle({ job }) {
40
40
  ] })
41
41
  ] });
42
42
  }
43
+ function DealsBlock({ deals }) {
44
+ if (deals.length === 0) return null;
45
+ return /* @__PURE__ */ jsxs("section", { className: "deals", "aria-label": "Deals", children: [
46
+ /* @__PURE__ */ jsx("h2", { children: "Deals" }),
47
+ /* @__PURE__ */ jsx("p", { className: "meta", children: "We may earn a commission when you use these links." }),
48
+ /* @__PURE__ */ jsx("div", { className: "list", children: deals.map((deal) => {
49
+ const offer = deal.offers[0];
50
+ if (!offer) return null;
51
+ const stores = deal.offers.map((item) => item.merchant).filter(Boolean).join(", ");
52
+ return /* @__PURE__ */ jsxs("a", { className: "item", href: `/classifieds/out/${encodeURIComponent(offer.id)}`, children: [
53
+ /* @__PURE__ */ jsx("h2", { children: deal.title }),
54
+ /* @__PURE__ */ jsx("p", { className: "meta", children: [stores, dealDollars(deal.price_cents ?? offer.price_cents), deal.coupon_code ? `Code ${deal.coupon_code}` : null, deal.locality].filter(Boolean).join(" \xB7 ") })
55
+ ] }, deal.id);
56
+ }) })
57
+ ] });
58
+ }
43
59
  function ClassifiedsBoard({
44
60
  region,
45
61
  listings,
46
- category
62
+ category,
63
+ deals = []
47
64
  }) {
65
+ const dealsFirst = listings.length < 8;
48
66
  return /* @__PURE__ */ jsxs("div", { className: "np-market", children: [
49
67
  /* @__PURE__ */ jsx("h1", { children: "Classifieds" }),
50
68
  /* @__PURE__ */ jsxs("p", { className: "lead", children: [
@@ -60,13 +78,19 @@ function ClassifiedsBoard({
60
78
  /* @__PURE__ */ jsx("a", { className: !category ? "on" : void 0, href: "/classifieds", children: "All" }),
61
79
  CLASSIFIED_CATEGORIES.map((item) => /* @__PURE__ */ jsx("a", { className: category === item.value ? "on" : void 0, href: `/classifieds?category=${item.value}`, children: item.label }, item.value))
62
80
  ] }),
63
- listings.length === 0 ? /* @__PURE__ */ jsxs("section", { className: "empty", children: [
81
+ dealsFirst ? /* @__PURE__ */ jsx(DealsBlock, { deals }) : null,
82
+ listings.length === 0 ? deals.length === 0 ? /* @__PURE__ */ jsxs("section", { className: "empty", children: [
64
83
  /* @__PURE__ */ jsx("h2", { children: "No classifieds yet." }),
65
84
  /* @__PURE__ */ jsx("p", { children: "Post something neighbors might need." })
85
+ ] }) : /* @__PURE__ */ jsxs("p", { className: "meta", children: [
86
+ "No neighbor listings yet. ",
87
+ /* @__PURE__ */ jsx("a", { href: "/classifieds/post", children: "Place a listing" }),
88
+ "."
66
89
  ] }) : /* @__PURE__ */ jsx("div", { className: "list", children: listings.map((listing) => /* @__PURE__ */ jsxs("a", { className: "item", href: `/classifieds/${encodeURIComponent(listing.id)}`, children: [
67
90
  /* @__PURE__ */ jsx("h2", { children: listing.title }),
68
91
  /* @__PURE__ */ jsx("p", { className: "meta", children: [categoryLabel(listing.category), listing.location?.town, money(listing.price)].filter(Boolean).join(" \xB7 ") })
69
- ] }, listing.id)) })
92
+ ] }, listing.id)) }),
93
+ dealsFirst ? null : /* @__PURE__ */ jsx(DealsBlock, { deals })
70
94
  ] });
71
95
  }
72
96
  function ClassifiedArticle({ listing }) {
package/marketplace.css CHANGED
@@ -59,3 +59,5 @@
59
59
  color: inherit;
60
60
  }
61
61
  .np-market .filter a.on, .np-market .filter a:hover { border-color: var(--pub-ink, #171917); color: var(--pub-accent, #bb171d); }
62
+ .np-market .deals { margin: 0 0 28px; }
63
+ .np-market .deals h2 { font: 600 22px/1.2 var(--pub-serif); margin: 0 0 8px; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scalemule/news-publication",
3
- "version": "0.0.8",
3
+ "version": "0.0.9",
4
4
  "description": "Shared server framework for ScaleMule local news publications. Walnut Creek Times is the broadsheet default; each paper supplies its own identity and feature flags.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",