Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
e993b26
feat: 786 - add merch page
YulikK Mar 12, 2025
2e66950
feat: 786 - add fetch data and hero banner
YulikK Mar 19, 2025
9eafcbe
feat: 786 - add id and view
YulikK Mar 27, 2025
109650d
feat: 786 - add merch catalog widget with item list and styles
YulikK Apr 28, 2025
0647f7b
refactor: 786 - update merch button link to use relative path
YulikK Apr 30, 2025
bacb488
feat: 786 - merge main
YulikK May 8, 2025
ed4c779
refactor: 786 - restructure merch module and improve component reusab…
YulikK May 8, 2025
ef8479a
refactor: 786 - remove hardcoded CDN URL and use base URL from env
YulikK May 8, 2025
098bc07
feat: 786 - add archive download functionality for merch items
YulikK May 9, 2025
b647fbc
refactor: 786 - replace ROUTES.MERCH with LINKS.MERCH for consistency
YulikK May 9, 2025
3cacc8f
refactor: 786 - change product ID from string to number and remove uu…
YulikK May 14, 2025
451f808
fix: 786 - update merch button link in test constants
YulikK May 14, 2025
eb3e9b0
refactor: 786 - improve image handling and structure
YulikK May 14, 2025
d6a751a
refactor: 786 - rename CommunityRoute to MerchRoute and streamline me…
YulikK Jun 3, 2025
4ad14a4
feat: 786 - Merge branch main
YulikK Jun 3, 2025
2b6cf78
feat: 786 - merge main
YulikK Jul 17, 2025
1100de6
fix: 786 - update base URL handling in transformMerchCatalog function
YulikK Jul 26, 2025
79f71d4
feat: 786 - enhance MerchCard with loading indicators and improved do…
YulikK Jul 27, 2025
59d144b
feat: 786 - update MerchCatalog to handle empty state with appropriat…
YulikK Jul 27, 2025
77ff5ac
feat: 786 - improve MerchStore to cache loaded merch catalog for bett…
YulikK Jul 27, 2025
29b539c
fix: 786 - update downloadFile function to correctly set download att…
YulikK Jul 27, 2025
955ed0a
fix: 786 - update merch-card styles for consistent opacity handling a…
YulikK Jul 27, 2025
377d96f
feat: 786 - merge branch main
YulikK Aug 3, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions dev-data/hero-page.data.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import coursesPageHeroImg from '@/shared/assets/mentor-with-his-students.webp';
import welcome from '@/shared/assets/welcome.webp';

export const heroPageData = {
school: {
Expand Down Expand Up @@ -26,4 +27,11 @@ export const heroPageData = {
subTitle: ['By teaching others, you learn yourself'],
imageAltText: '',
},
merch: {
mainTitle: 'Merch',
widgetTitle: 'Free assets for your design',
subTitle: [''],
heroImageSrc: welcome,
imageAltText: 'A sloth mascot with arms raised under a welcome sign',
},
};
2,934 changes: 1,725 additions & 1,209 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"dayjs": "^1.11.13",
"github-markdown-css": "^5.8.1",
"http-status": "^2.1.0",
"jszip": "^3.10.1",
"next": "15.4.5",
"pagefind": "^1.3.0",
"react": "19.1.1",
Expand Down
264 changes: 132 additions & 132 deletions src/app/docs/components/search/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,237 +17,237 @@ declare global {
}

declare global {
/** Global index options that can be passed to pagefind.options() */
type PagefindIndexOptions = {
/** Overrides the URL path that Pagefind uses to load its search bundle */
basePath?: string;
/** Appends the given baseURL to all search results. May be a path, or a full domain */
baseUrl?: string;
/** The maximum length of excerpts that Pagefind should generate for search results. Default to 30 */
excerptLength?: number;
/**
/** Global index options that can be passed to pagefind.options() */
type PagefindIndexOptions = {
/** Overrides the URL path that Pagefind uses to load its search bundle */
basePath?: string;
/** Appends the given baseURL to all search results. May be a path, or a full domain */
baseUrl?: string;
/** The maximum length of excerpts that Pagefind should generate for search results. Default to 30 */
excerptLength?: number;
/**
* Multiply all rankings for this index by the given weight.
*
* Only applies in multisite setups, where one site should rank higher or lower than others.
*/
indexWeight?: number;
/**
indexWeight?: number;
/**
* Merge this filter object into all search queries in this index.
*
* Only applies in multisite setups.
*/
mergeFilter?: object;
/**
mergeFilter?: object;
/**
* If set, will ass the search term as a query parameter under this key, for use with Pagefind's highlighting script.
*/
highlightParam?: string;
language?: string;
/**
highlightParam?: string;
language?: string;
/**
* Whether an instance of Pagefind is the primary index or not (for multisite).
*
* This is set for you automatically, so it is unlikely you should set this directly.
*/
primary?: boolean;
/**
primary?: boolean;
/**
* Provides the ability to fine tune Pagefind's ranking algorithm to better suit your dataset.
*/
ranking?: PagefindRankingWeights;
};
ranking?: PagefindRankingWeights;
};

type PagefindRankingWeights = {
/**
type PagefindRankingWeights = {
/**
Controls page ranking based on similarity of terms to the search query (in length).
Increasing this number means pages rank higher when they contain words very close to the query,
e.g. if searching for `part` then `party` will boost a page higher than one containing `partition`.
Minimum value is 0.0, where `party` and `partition` would be viewed equally.
*/
termSimilarity?: number;
/**
termSimilarity?: number;
/**
Controls how much effect the average page length has on ranking.
Maximum value is 1.0, where ranking will strongly favour pages that are shorter than the average page on the site.
Minimum value is 0.0, where ranking will exclusively look at term frequency, regardless of how long a document is.
*/
pageLength?: number;
/**
pageLength?: number;
/**
Controls how quickly a term saturates on the page and reduces impact on the ranking.
Maximum value is 2.0, where pages will take a long time to saturate, and pages with very high term frequencies will take over.
As this number trends to 0, it does not take many terms to saturate and allow other paramaters to influence the ranking.
Minimum value is 0.0, where terms will saturate immediately and results will not distinguish between one term and many.
*/
termSaturation?: number;
/**
termSaturation?: number;
/**
Controls how much ranking uses term frequency versus raw term count.
Maximum value is 1.0, where term frequency fully applies and is the main ranking factor.
Minimum value is 0.0, where term frequency does not apply, and pages are ranked based on the raw sum of words and weights.
Values between 0.0 and 1.0 will interpolate between the two ranking methods.
Reducing this number is a good way to boost longer documents in your search results, as they no longer get penalized for having a low term frequency.
*/
termFrequency?: number;
};
termFrequency?: number;
};

/** Options that can be passed to pagefind.search() */
type PagefindSearchOptions = {
/** If set, this call will load all assets but return before searching. Prefer using pagefind.preload() instead */
preload?: boolean;
/** Add more verbose console logging for this search query */
verbose?: boolean;
/** The set of filters to execute with this search. Input type is extremely flexible, see the filtering docs for details */
filters?: object;
/** The set of sorts to use for this search, instead of relevancy */
sort?: object;
};
/** Options that can be passed to pagefind.search() */
type PagefindSearchOptions = {
/** If set, this call will load all assets but return before searching. Prefer using pagefind.preload() instead */
preload?: boolean;
/** Add more verbose console logging for this search query */
verbose?: boolean;
/** The set of filters to execute with this search. Input type is extremely flexible, see the filtering docs for details */
filters?: object;
/** The set of sorts to use for this search, instead of relevancy */
sort?: object;
};

/** Filter counts returned from pagefind.filters(), and alongside results from pagefind.search() */
type PagefindFilterCounts = Record<string, Record<string, number>>;
/** Filter counts returned from pagefind.filters(), and alongside results from pagefind.search() */
type PagefindFilterCounts = Record<string, Record<string, number>>;

/** The main results object returned from a call to pagefind.search() */
type PagefindSearchResults = {
/** All pages that match the search query and filters provided */
results: PagefindSearchResult[];
/** How many results would there have been if you had omitted the filters */
unfilteredResultCount: number;
/** Given the query and filters provided, how many remaining results are there under each filter? */
filters: PagefindFilterCounts;
/** If the searched filters were removed, how many total results for each filter are there? */
totalFilters: PagefindFilterCounts;
/** Information on how long it took Pagefind to execute this query */
timings: {
preload: number;
search: number;
total: number;
};
/** The main results object returned from a call to pagefind.search() */
type PagefindSearchResults = {
/** All pages that match the search query and filters provided */
results: PagefindSearchResult[];
/** How many results would there have been if you had omitted the filters */
unfilteredResultCount: number;
/** Given the query and filters provided, how many remaining results are there under each filter? */
filters: PagefindFilterCounts;
/** If the searched filters were removed, how many total results for each filter are there? */
totalFilters: PagefindFilterCounts;
/** Information on how long it took Pagefind to execute this query */
timings: {
preload: number;
search: number;
total: number;
};
};

/** A single result from a search query, before actual data has been loaded */
type PagefindSearchResult = {
/** Pagefind's internal ID for this page, unique across the site */
id: string;
/** Pagefind's internal score for your query matching this page, that is used when ranking these results */
score: number;
/** The locations of all matching words in this page */
words: number[];
/**
/** A single result from a search query, before actual data has been loaded */
type PagefindSearchResult = {
/** Pagefind's internal ID for this page, unique across the site */
id: string;
/** Pagefind's internal score for your query matching this page, that is used when ranking these results */
score: number;
/** The locations of all matching words in this page */
words: number[];
/**
* Calling data() loads the final data fragment needed to display this result.
*
* Only call this when you need to display the data, rather than all at once.
* (e.g. one page as a time, or in a scroll listener)
* */
data: () => Promise<PagefindSearchFragment>;
};
data: () => Promise<PagefindSearchFragment>;
};

/** The useful data Pagefind provides for a search result */
type PagefindSearchFragment = {
/** Pagefind's processed URL for this page. Will include the baseUrl if configured */
url: string;
/** Pagefind's unprocessed URL for this page */
raw_url?: string;
/** The full processed content text of this page */
content: string;
/** Internal type β€” ignore for now */
raw_content?: string;
/** The processed excerpt for this result, with matching terms wrapping in `<mark>` elements */
excerpt: string;
/**
/** The useful data Pagefind provides for a search result */
type PagefindSearchFragment = {
/** Pagefind's processed URL for this page. Will include the baseUrl if configured */
url: string;
/** Pagefind's unprocessed URL for this page */
raw_url?: string;
/** The full processed content text of this page */
content: string;
/** Internal type β€” ignore for now */
raw_content?: string;
/** The processed excerpt for this result, with matching terms wrapping in `<mark>` elements */
excerpt: string;
/**
* What regions of the page matched this search query?
*
* Precalculates based on h1->6 tags with IDs, using the text between each.
*/
sub_results: PagefindSubResult[];
/** How many total words are there on this page? */
word_count: number;
/** The locations of all matching words in this page */
locations: number[];
/**
sub_results: PagefindSubResult[];
/** How many total words are there on this page? */
word_count: number;
/** The locations of all matching words in this page */
locations: number[];
/**
* The locations of all matching words in this page,
* paired with data about their weight and relevance to this query
*/
weighted_locations: PagefindWordLocation[];
/** The filter keys and values this page was tagged with */
filters: Record<string, string[]>;
/** The metadata keys and values this page was tagged with */
meta: Record<string, string>;
/**
weighted_locations: PagefindWordLocation[];
/** The filter keys and values this page was tagged with */
filters: Record<string, string[]>;
/** The metadata keys and values this page was tagged with */
meta: Record<string, string>;
/**
* The raw anchor data that Pagefind used to generate sub_results.
*
* Contains _all_ elements that had IDs on the page, so can be used to
* implement your own sub result calculations with different semantics.
*/
anchors: PagefindSearchAnchor[];
};
anchors: PagefindSearchAnchor[];
};

/** Data for a matched section within a page */
type PagefindSubResult = {
/**
/** Data for a matched section within a page */
type PagefindSubResult = {
/**
* Title of this sub result β€” derived from the heading content.
*
* If this is a result for the section of the page before any headings with IDs,
* this will be the same as the page's meta.title value.
*/
title: string;
/**
title: string;
/**
* Direct URL to this sub result, comprised of the page's URL plus the hash string of the heading.
*
* If this is a result for the section of the page before any headings with IDs,
* this will be the same as the page URL.
*/
url: string;
/** The locations of all matching words in this segment */
locations: number[];
/**
url: string;
/** The locations of all matching words in this segment */
locations: number[];
/**
* The locations of all matching words in this segment,
* paired with data about their weight and relevance to this query
*/
weighted_locations: PagefindWordLocation[];
/** The processed excerpt for this segment, with matching terms wrapping in `<mark>` elements */
excerpt: string;
/**
weighted_locations: PagefindWordLocation[];
/** The processed excerpt for this segment, with matching terms wrapping in `<mark>` elements */
excerpt: string;
/**
* Raw data about the anchor element associated with this sub result.
*
* The omission of this field means this sub result is for text found on the page
* before the first heading that had an ID.
*/
anchor?: PagefindSearchAnchor;
};
anchor?: PagefindSearchAnchor;
};

/** Information about a matching word on a page */
type PagefindWordLocation = {
/** The weight that this word was originally tagged as */
weight: number;
/**
/** Information about a matching word on a page */
type PagefindWordLocation = {
/** The weight that this word was originally tagged as */
weight: number;
/**
* An internal score that Pagefind calculated for this word.
*
* The absolute value is somewhat meaningless, but the value can be used
* in comparison to other values in this set of search results to perform custom ranking.
*/
balanced_score: number;
/**
balanced_score: number;
/**
* The index of this word in the result content.
*
* Splitting the content key by whitespacing and indexing by this number
* will yield the correct word.
*/
location: number;
};
location: number;
};

/** Raw data about elements with IDs that Pagefind encountered when indexing the page */
type PagefindSearchAnchor = {
/** What element type was this anchor? e.g. `h1`, `div` */
element: string;
/** The raw id="..." attribute contents of the element */
id: string;
/**
/** Raw data about elements with IDs that Pagefind encountered when indexing the page */
type PagefindSearchAnchor = {
/** What element type was this anchor? e.g. `h1`, `div` */
element: string;
/** The raw id="..." attribute contents of the element */
id: string;
/**
* The text content of this element.
*
* In order to prevent repeating most of the page data for every anchor,
* Pagefind will only take top level text nodes, or text nodes nested within
* inline elements such as <a> and <span>.
*/
text?: string;
/**
text?: string;
/**
* The position of this anchor in the result content.
* Splitting the content key by whitespacing and indexing by this number
* will yield the first word indexed after this element's ID was found.
*/
location: number;
};
location: number;
};
}
Loading
Loading