diff --git a/README.md b/README.md
index b73242e0..ae39ae5e 100644
--- a/README.md
+++ b/README.md
@@ -41,7 +41,7 @@ The following properties use the UniNav for the MVP:
### Navigation Types
-The UniNav comprises 3 parts, `'marketing' | 'tool' | 'footer'`, which are described here.
+The UniNav comprises 4 variants, `'marketing' | 'tool' | 'topgear' | 'footer'`, which are described here.
#### Marketing Navigation
@@ -55,6 +55,21 @@ This navigation is rendered for all non-marketing web properites (e.g https://pl
The navigation configuration is supplied upon initialization by the tool itself. See [2. Initialize tcUnNav](#2-initialize-tcuninav) for more info.
+#### Topgear Navigation
+
+Tool navigation automatically uses the Topgear header on exactly
+`topgear.topcoder.com` and `topgear.topcoder-dev.com`. Integrations can also
+request `type: 'topgear'`; it falls back to the tool header on any other host.
+Marketing and footer navigation are unaffected.
+
+The responsive header reuses community-app's Wipro, Topcoder, and Topgear logos.
+Home links to `https://topgear-app.wipro.com`; Challenges links to
+`/opportunities/challenge` on the current Topgear origin. It retains account
+controls, without the tool selector, Leaderboard, or the community hero banner.
+Deploy the navigation bundle before the Platform UI migration. Its CloudFront
+CORS policy must allow the matching Topgear origin (included in the IAC template).
+This change does not redirect production community-app URLs.
+
#### Footer Navigation
This navigation is rendered for all web properties, regardless if they're marketing sites or tools.
@@ -292,7 +307,7 @@ Information regarding the changes will be disseminated to the owners of all prop
| config.toolName | string | yes (tool nav only) | The name of the tool as it should appear in the header | none |
| config.toolRoot | string | yes (tool nav only) | The route to the tool as it should appear in the header | none |
| config.fullFooter | boolean | no | Shows the full footer navigation when it is true | false |
-| config.type | Enum: 'marketing' \| 'tool' \| 'footer' | yes | The type of navigation to render | |
+| config.type | Enum: 'marketing' \| 'tool' \| 'topgear' \| 'footer' | yes | The type of navigation to render | |
| config.user | 'auto' | no | Instruct the uni-nav to auto-fetch user profile info based on the `tcjwt` cookie value | {} |
| config.user | {photoURL, userId, initials, handle} | no | The logged in user | {} |
| config.supportMeta | {challengeId, isSelfService} | no | Additional meta data to be sent along with any support ticket created by the user | |
diff --git a/iac/uni-nav.deploy.yml b/iac/uni-nav.deploy.yml
index 30fc34dc..955ca730 100644
--- a/iac/uni-nav.deploy.yml
+++ b/iac/uni-nav.deploy.yml
@@ -161,6 +161,9 @@ Resources:
# Platform UI Prod & Dev
- !Sub https://platform-ui.${Domain}
+ # Topgear community on Platform UI
+ - !Sub https://topgear.${Domain}
+
# Platform UI sub apps for Prod & Dev
- !Sub https://academy.${Domain}
- !Sub https://work.${Domain}
diff --git a/package.json b/package.json
index 3779817d..a89463e9 100644
--- a/package.json
+++ b/package.json
@@ -10,6 +10,8 @@
"build:nudge": "APP_BUILD_TARGET=nudge vite build --mode=production",
"build:base": "vite build --mode=production && npx rimraf ./types && npm run types",
"build": "npm run build:base && npm run build:nudge",
+ "lint": "npm run check",
+ "test": "node --experimental-strip-types --test tests/*.test.mjs",
"check": "svelte-check --tsconfig ./tsconfig.json",
"demo:dist": "npx http-server --cors --host local.topcoder-dev.com --port 8083 ./dist",
"demo:marketing": "npx http-server --host local.topcoder-dev.com --port 8081 ./demo/marketing -P http://local.topcoder-dev.com:8081? -o /",
diff --git a/src/README.md b/src/README.md
index e8c78c37..b8a30e3e 100644
--- a/src/README.md
+++ b/src/README.md
@@ -105,3 +105,9 @@ Both the types commit and the version patch commit will be authored by the user
For non-master branches, there is a build job in the CI that will check to see if there are any updates to the types required and exit with an error code 1 with message `WARNING: Types need updating`.
The error will not prevent merging a PR or deploying to an environment but will just be a flag for the PR owner and reviewers.
+
+## Validation
+
+Run `npm run lint` (Svelte/TypeScript diagnostics), `npm test` (hostname
+selection regressions), and `npm run build` before proposing a change.
+Tests use the Node version in `.nvmrc` and its TypeScript stripping support.
diff --git a/src/lib/components/user-area/UserArea.svelte b/src/lib/components/user-area/UserArea.svelte
index e0cbd830..17174a1d 100644
--- a/src/lib/components/user-area/UserArea.svelte
+++ b/src/lib/components/user-area/UserArea.svelte
@@ -15,6 +15,9 @@
import Completedness from './Completedness.svelte';
import Button from '../Button.svelte';
+ /** Whether to offer the cross-application tool selector; hidden in community headers. */
+ export let showToolSelector = true;
+
const ctx = getAppContext();
// debounce updates to user if user.handle stays the same
@@ -98,7 +101,7 @@
{:else }
-
+ {#if showToolSelector}{/if}
{
+ const loadAnimation = (path: string) => {
var animData = {
container: ref,
renderer: 'svg',
@@ -25,7 +25,7 @@
progressiveLoad: true
},
};
- const bmAnim = window['bodymovin'].loadAnimation(animData);
+ const bmAnim = (window as any).bodymovin.loadAnimation(animData);
bmAnim.addEventListener('data_ready', () => {
if (coverRef) {
Object.assign(coverRef.style, {display: 'none'});
diff --git a/src/lib/topgear-navigation/TopgearNavigation.module.scss b/src/lib/topgear-navigation/TopgearNavigation.module.scss
new file mode 100644
index 00000000..d21ddd39
--- /dev/null
+++ b/src/lib/topgear-navigation/TopgearNavigation.module.scss
@@ -0,0 +1,77 @@
+@use 'lib/styles/fonts.scss' as *;
+
+.header {
+ display: flex;
+ align-items: center;
+ gap: 32px;
+ min-height: 110px;
+ padding: 20px 40px;
+ background: #fff;
+ color: #00a2e0;
+ font-family: $roboto;
+}
+
+.branding {
+ display: flex;
+ align-items: center;
+ gap: 30px;
+
+ a { display: flex; flex-shrink: 0; }
+}
+
+.wipro { width: 90px; }
+.topcoder { width: 135px; }
+.topgear { width: 132px; }
+
+.header img { height: auto; }
+
+.links {
+ display: flex;
+ align-items: center;
+ gap: 40px;
+ margin-right: auto;
+ font-size: 18px;
+ font-weight: 700;
+
+ a {
+ position: relative;
+ padding: 12px 0;
+ }
+
+ a[aria-current='page']::after {
+ content: '';
+ position: absolute;
+ bottom: 0;
+ left: calc(50% - 4px);
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ background: currentColor;
+ }
+}
+
+.header a:focus-visible {
+ outline: 2px solid #16679a;
+ outline-offset: 4px;
+}
+
+.user { flex-shrink: 0; }
+
+@media (max-width: 1023px) {
+ .header { gap: 24px; padding: 16px; }
+ .branding { gap: 16px; }
+ .links { gap: 24px; }
+ .wipro { width: 70px; }
+ .topcoder { width: 110px; }
+ .topgear { width: 100px; }
+}
+
+@media (max-width: 767px) {
+ .header { flex-wrap: wrap; gap: 16px; }
+ .branding { flex: 1 0 calc(100% - 106px); gap: 12px; }
+ .wipro { width: 64px; }
+ .topcoder { width: 100px; }
+ .topgear { order: 1; width: 90px; }
+ .links { order: 2; font-size: 16px; }
+ .user { order: 3; }
+}
diff --git a/src/lib/topgear-navigation/TopgearNavigation.svelte b/src/lib/topgear-navigation/TopgearNavigation.svelte
new file mode 100644
index 00000000..d2d8fb20
--- /dev/null
+++ b/src/lib/topgear-navigation/TopgearNavigation.svelte
@@ -0,0 +1,34 @@
+
+
+
+