diff --git a/.assets-revision b/.assets-revision index 35808cfa..b3ee59d9 100644 --- a/.assets-revision +++ b/.assets-revision @@ -5,4 +5,4 @@ # is a git revision (branch name like `main`, a tag, or a specific commit # sha). Override at runtime with the ASSETS_REVISION env var. repo: ChilleD/WebHarbor -revision: 8d8e4069588ef55594622fee1ab9fa51c2011d07 +revision: refs/pr/54 diff --git a/.dockerignore b/.dockerignore index a921bf15..1c7a6ffe 100644 --- a/.dockerignore +++ b/.dockerignore @@ -8,6 +8,9 @@ sites/*/instance/ # Don't ship — runtime doesn't need scrape intermediate (data is in instance_seed/*.db). sites/*/scraped_data/ +# Don't ship — per-site local dev helpers (asset harvest, smoke tests, leak audits). +sites/*/scripts_dev/ + # Don't ship — bytecode / venvs. sites/*/__pycache__/ **/__pycache__/ diff --git a/.gitignore b/.gitignore index e7899232..3798c34a 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ sites/*/static/external_cache/ # ============================================================= # scrape pipeline intermediate; runtime data lives in instance_seed/*.db sites/*/scraped_data/ +/scraped_data/ # rebuilt at every container boot from instance_seed/ sites/*/instance/ sites/*/venv/ diff --git a/AGENTS.md b/AGENTS.md index 8f6e45b8..14e45939 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,7 +4,7 @@ A coding agent (Claude Code, Cursor, Aider, Codex, ...) is reading this. Read on ## What it is -19 Flask mirror websites (Amazon, GitHub, BBC News, ...) packaged into one Docker image, plus a control plane on `:8101` for resetting per-site state. Used as a deterministic offline environment for web-agent benchmarks. ~3 GB image. +20 Flask mirror websites (Amazon, GitHub, BBC News, ...) packaged into one Docker image, plus a control plane on `:8101` for resetting per-site state. Used as a deterministic offline environment for web-agent benchmarks. ~3 GB image. Two repos: - **code** (this one) — Flask apps, control plane, scripts. @@ -48,17 +48,17 @@ Inside the image, sites live at `/opt/WebSyn//`. The path predates the ren # fresh clone ./scripts/fetch_assets.sh # pulls assets from HF ./scripts/build.sh # docker build -t webharbor:dev . -docker run -d -p 8101:8101 -p 40000-40018:40000-40018 webharbor:dev +docker run -d -p 8101:8101 -p 40000-40019:40000-40019 webharbor:dev ``` Or use the published image directly: ```bash -docker run -d -p 8101:8101 -p 40000-40018:40000-40018 \ +docker run -d -p 8101:8101 -p 40000-40019:40000-40019 \ battalion7244/webharbor:latest ``` -Sites are on `40000`-`40018` in the order declared by `SITES=( ... )` in `websyn_start.sh`. Control plane: +Sites are on `40000`-`40019` in the order declared by `SITES=( ... )` in `websyn_start.sh`. Control plane: | Method | Path | Purpose | |--------|---------------------|-------------------------------------------| @@ -136,13 +136,13 @@ python3 -m py_compile sites//app.py # 3. run on alt ports (don't collide with anything you already have running) docker run -d --rm --name wh-test \ - -p 8201:8101 -p 41000-41018:40000-40018 webharbor:dev + -p 8201:8101 -p 41000-41019:40000-40019 webharbor:dev # 4. control plane healthy, all sites alive curl -s http://localhost:8201/health | python3 -m json.tool | head # 5. every site renders 200 -for p in $(seq 41000 41018); do +for p in $(seq 41000 41019); do curl -so /dev/null -w "$p:%{http_code}\n" http://localhost:$p/ done diff --git a/CLAUDE.md b/CLAUDE.md index 3f6b490e..5dcf2283 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -16,4 +16,4 @@ The full agent guide is loaded above via `@AGENTS.md`. The notes below apply onl ## Existing containers -If a container is already running on `:8101` / `:40000-40018`, treat it as the user's working environment — don't `docker stop` or `docker rm` it without explicit confirmation. Spin up your test container under a different name on alt ports (`:8201`, `:41000-41018`). +If a container is already running on `:8101` / `:40000-40019`, treat it as the user's working environment — don't `docker stop` or `docker rm` it without explicit confirmation. Spin up your test container under a different name on alt ports (`:8201`, `:41000-41019`). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 07a7248b..6dce56be 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -24,7 +24,7 @@ git clone https://github.com//webharbor && cd webharbor ./scripts/fetch_assets.sh # pull current assets ./scripts/new_site.py mywebsite # OR edit an existing site ./scripts/build.sh && docker run -d --rm \ - -p 8101:8101 -p 40000-40018:40000-40018 webharbor:dev + -p 8101:8101 -p 40000-40019:40000-40019 webharbor:dev # iterate locally... ./scripts/extract_assets.sh ../webharbor-static-pr/ # split assets out diff --git a/Dockerfile b/Dockerfile index 24105b6a..91383cb7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # WebHarbor — slim, self-contained image. -# 19 Flask mirror sites + control plane on :8101. +# 20 Flask mirror sites + control plane on :8101. FROM python:3.12-slim-bookworm @@ -40,6 +40,6 @@ COPY control_server.py /opt/control_server.py COPY site_runner.py /opt/site_runner.py RUN chmod +x /opt/websyn_start.sh -EXPOSE 8101 40000-40018 +EXPOSE 8101 40000-40019 CMD ["/opt/websyn_start.sh"] diff --git a/README.md b/README.md index acf7bf0e..c26c17e1 100644 --- a/README.md +++ b/README.md @@ -36,17 +36,17 @@ WebHarbor takes a different approach. We leverage coding agent (e.g., Claude Cod - **Deep features unlocked** — carts, checkouts, accounts, all fully testable - **Evolving** — harder tasks drive richer mirrors; the environment grows with agents - **RL-ready** — sub-second database resets between rollouts -- **Community-driven** — 19 sites today, scaling to 100+ together +- **Community-driven** — 20 sites today, scaling to 100+ together ## 🚀 Quickstart One command to run all web environments: ```bash -docker run -p 8101:8101 -p 40000-40018:40000-40018 battalion7244/webharbor:latest +docker run -p 8101:8101 -p 40000-40019:40000-40019 battalion7244/webharbor:latest ``` -Then point your agent at `http://localhost:40000` through `http://localhost:40018` to explore 19 local mirrors of webvoyager sites: `Allrecipes, Amazon, Apple, ArXiv, BBC News, Booking, GitHub, Google Flights, Google Maps, Google Search, Hugging Face, Wolfram Alpha, Cambridge Dictionary, Coursera, ESPN, Merriam-Webster, IKEA, Phys.org, and Target`. +Then point your agent at `http://localhost:40000` through `http://localhost:40019` to explore 20 local mirrors of webvoyager sites: `Allrecipes, Amazon, Apple, ArXiv, BBC News, Booking, GitHub, Google Flights, Google Maps, Google Search, Hugging Face, Wolfram Alpha, Cambridge Dictionary, Coursera, ESPN, Merriam-Webster, IKEA, Phys.org, Target, and Walmart Careers`. For sub-second reset between rollouts, expose the control plane and call `/reset/`: @@ -65,7 +65,7 @@ git clone https://github.com/aiming-lab/WebHarbor && cd WebHarbor ## 🤝 Contribute -We have built 18 high-quality mirrors covering the [WebVoyager](https://github.com/MinorJerry/WebVoyager) benchmark. The next goal is **100+ sites**, covering everything in [Online-Mind2Web](https://huggingface.co/datasets/osunlp/Online-Mind2Web). We are inviting the community to build this together. +We have built 20 high-quality mirrors covering the [WebVoyager](https://github.com/MinorJerry/WebVoyager) benchmark. The next goal is **100+ sites**, covering everything in [Online-Mind2Web](https://huggingface.co/datasets/osunlp/Online-Mind2Web). We are inviting the community to build this together. There are two ways to join the author list: diff --git a/agent_demo/README.md b/agent_demo/README.md index 2b775eac..a8a1a5c2 100644 --- a/agent_demo/README.md +++ b/agent_demo/README.md @@ -19,7 +19,7 @@ export OPENAI_BASE_URL=https://api.openai.com/v1 # or your Azure / vLLM endpoi ## Run a task -WebHarbor must already be running locally (`docker run -p 8101:8101 -p 40000-40018:40000-40018 battalion7244/webharbor:latest`). +WebHarbor must already be running locally (`docker run -p 8101:8101 -p 40000-40019:40000-40019 battalion7244/webharbor:latest`). Run a single task from a site's `tasks.jsonl`: diff --git a/control_server.py b/control_server.py index af39b036..1476c29c 100644 --- a/control_server.py +++ b/control_server.py @@ -27,6 +27,7 @@ 'github', 'google_flights', 'google_map', 'google_search', 'huggingface', 'wolfram_alpha', 'cambridge_dictionary', 'coursera', 'espn', 'merriam_webster', 'ikea', 'phys_org', 'target', + 'walmart_careers', ] BASE_PORT = 40000 WEBSYN_DIR = '/opt/WebSyn' diff --git a/sites/walmart_careers/.gitignore b/sites/walmart_careers/.gitignore new file mode 100644 index 00000000..a34abb5d --- /dev/null +++ b/sites/walmart_careers/.gitignore @@ -0,0 +1,2 @@ +# Local build/inspection helpers: never shipped with the site. +scripts_dev/ diff --git a/sites/walmart_careers/CLAUDE.md b/sites/walmart_careers/CLAUDE.md new file mode 100644 index 00000000..72d5adfe --- /dev/null +++ b/sites/walmart_careers/CLAUDE.md @@ -0,0 +1,80 @@ +# walmart_careers — site notes + +Mirror of https://careers.walmart.com. Port **40019** (index 19 in `websyn_start.sh`, the 20th and last site); +alt-port test container maps it to **41019**. + +## Layout + +| file | role | +|---|---| +| `app.py` | models, routes, scored search, deterministic SVG maps, bootstrap | +| `catalog_source.py` | the source catalog: areas, categories, 51 stores, 37 hourly + 29 salaried title families with explicit placements, hub copy, trending job ids | +| `seed_data.py` | turns the catalog into SQLite; `build_seed_database()` is the freezer | +| `_content.py` | static chrome strings only: headings, boilerplate prose, design constants, US/PR map outlines | +| `templates/` | 20 Jinja2 templates (`base.html`, the stripped `base_auth.html` for sign-in/register) + the `_job_card.html`, `_benefits.html` and `_life.html` macros shared by the detail, career-area and About pages | +| `static/` | `css/`, `js/`, `icons/`, `fonts/` in git; `images/` HF-managed | +| `scripts_dev/` | local-only helpers and build-time invariants; gitignored and dockerignored | + +Everything a handler renders about a job, a store or a hub comes from SQLAlchemy. +`_content.py` holds no per-record content: hub name/blurb/image live on `Store`, +and the trending flag lives on `Job.is_trending`. + +## Rebuilding the seed DB + +```bash +cd sites/walmart_careers +PYTHONHASHSEED=0 python seed_data.py # writes instance_seed/walmart_careers.db +``` + +Run it twice and compare md5s — the build is byte-reproducible. + +`build_seed_database()` also runs the build-time invariant checks, which fail the +build if a catalog edit breaks a volume invariant (jobs per category/store/state/ +shift) or a benchmark task's near-miss set. Those checks live in +`scripts_dev/assert_distractors.py`, which the freezer loads by path *only if the +file exists* — it is git-ignored and docker-ignored, so it never reaches the shipped +tree, and a checkout without it builds the identical database and prints a note that +the checks were skipped. Nothing in `seed_data.py` or `app.py` encodes what a task +is looking for. The checks never run at import, bootstrap or `/reset` time. + +## Determinism rules that must hold + +- one RNG: `random.Random(20260905)` in `seed_data.py`, nothing else +- `MIRROR_REFERENCE_DATE` instead of `date.today()`; no `utcnow()`/`now()` anywhere on the + import or bootstrap path (runtime writes may use `now()` — `/reset` wipes them) +- the four werkzeug password hashes are hard-coded (werkzeug salts randomly) +- `seed_database()` and `seed_benchmark_users()` are each gated as a whole + +## Local dev helpers (`scripts_dev/`, never shipped) + +```bash +python scripts_dev/serve.py 5017 # run with Jinja auto-reload on +python scripts_dev/walkthrough.py [] +python scripts_dev/leak_audit.py # writes leak_audit.md +python scripts_dev/robustness.py +python scripts_dev/shots.py # 1440px screenshots +``` + +`assert_distractors.py` is not run directly — the freezer imports it by path. + +`assert_distractors.py`, `walkthrough.py`, `leak_audit.py`, `robustness.py` and +`VERIFICATION.md` hold the ground-truth answers, which is exactly why `scripts_dev/` +is in both `.gitignore` and `.dockerignore`. + +## Assets + +Brand chrome (`static/icons/`, `static/fonts/`) is committed; photography +(`static/images/`) is HF-managed. Everything was pulled from the live site: +`cms.careers.walmart.com/content/dam/careers/...`, `careers.walmart.com/assets/svgs/...`, +and the `EverydaySansUI` / `LivingDesign` font files from `i5.walmartimages.com`. +`scripts_dev/harvest_assets.py` records the exact URL → filename mapping and re-downloads +them; images are then downscaled to 1600px wide. Upstream carries no Dallas hub photo, so +`loc-dallas.jpg` is the Global Tech office interior from the same CMS (see `harvest_assets.py`). + +## Things that are deliberately not mirrored + +Google Maps (replaced by a deterministic server-rendered SVG cluster map on `/results` +and an SVG pin card on the detail page), the LLM search assistant (replaced by ordinary +query params with token-overlap scoring), Workday/OIDC login (local email + password), +the OTP apply flow (two-step local form), and the Future roles / Content tabs (explicit +empty-state panels). diff --git a/sites/walmart_careers/_content.py b/sites/walmart_careers/_content.py new file mode 100644 index 00000000..dff3a42a --- /dev/null +++ b/sites/walmart_careers/_content.py @@ -0,0 +1,684 @@ +"""Static CMS-style copy and design constants for the Walmart Careers mirror. + +Nothing in here is runtime data: these are the marketing strings, benefit tiles +and map outlines that the real site serves from AEM. Runtime data (jobs, stores, +users, saved roles, applications) lives in SQLite only. +""" +from __future__ import annotations + +from datetime import date + +# The mirror is frozen against this date. Never call date.today() anywhere in +# the seed or bootstrap path. +MIRROR_REFERENCE_DATE = date(2026, 8, 31) + +SITE_NAME = "Walmart Careers" +COPYRIGHT = "©2026 Walmart Inc." + +HERO_HEADLINE_1 = "Cashiers wanted." +HERO_HEADLINE_2 = "Next move, yours." +SEARCH_PLACEHOLDER = "Search by team, department, keyword" + +VALUES = [ + ("Respect for the individual", "We listen, we support, and we help each other grow."), + ("Service to the customer", "Everything starts with the people who shop with us."), + ("Strive for excellence", "We look for a better way, every single day."), + ("Act with integrity", "We do the right thing, especially when it is hard."), +] + +BENEFIT_ROWS = [ + ("Financial perks", "Enjoy 401(k) matching and stock purchase plans.", "benefit-financial.svg"), + ("Paid time off", "Take a break as needed for vacations, sick leave, holidays, parental leave and more.", "benefit-pto.svg"), + ("Comprehensive health benefits", "Medical, dental, vision and wellness programs for you and your family.", "benefit-health.svg"), + ("Wellbeing programs", "Access mental health resources and assistance programs for life's challenges.", "benefit-wellbeing.svg"), + ("Career growth opportunities", "Training, leadership programs, and clear paths to advance.", "benefit-growth.svg"), +] + +# --------------------------------------------------------------------------- # +# Benefit tiles on the job detail page. Keyed by brand; hourly and salaried +# postings surface a slightly different Live Better U line, exactly as upstream. +# --------------------------------------------------------------------------- # +_WALMART_PLUS_TILE = ( + "Walmart+", + "Free shipping", + "As a Walmart Associate, you're eligible to become a Walmart+ member. Enjoy benefits " + "like free store delivery and shipping, fuel savings, and video streaming. Sam's Club " + "associates are eligible for a Club Membership.", + "tile-walmart-plus.svg", +) +_DISCOUNT_TILE = ( + "Discount Card", + "Get 10% off", + "Walmart associates are eligible for a 10% discount card on all general merchandise " + "items and fresh produce in-store and on select items at Walmart.com. Eligible after " + "90 days of employment.", + "tile-card.svg", +) +_LBU_FIELD_TILE = ( + "Live Better U", + "100% covered", + "Earn a degree or in-demand skills certificates with no debt - Walmart covers 100% of " + "tuition and books. Live Better U offers 60+ programs for Associates to pursue their dreams.", + "tile-graduation.svg", +) +_LBU_CORP_TILE = ( + "Live Better U", + "100% covered", + "Through Live Better U, Walmart and Sam's Club associates can learn critical skills and " + "create pathways for promotion into in-demand jobs within the company. Whether earning a " + "college degree, certificate or high school diploma, Walmart pays for tuition and books.", + "tile-graduation.svg", +) +_ACADEMY_TILE = ( + "Walmart Academy", + "Grow your skills", + "Ready to grow your career? Walmart Academy offers job-specific retail training and " + "leadership courses to help Associates reach their career goals.", + "tile-growth.svg", +) + + +def benefit_tiles_for(brand: str, population: str) -> list[tuple[str, str, str, str]]: + first = _DISCOUNT_TILE if population == "salaried" else _WALMART_PLUS_TILE + second = _LBU_CORP_TILE if population == "salaried" else _LBU_FIELD_TILE + return [first, second, _ACADEMY_TILE] + + +JOB_BENEFIT_ROWS = [ + ("Financial perks", "Enjoy 401(k) matching and stock purchase plans", "benefit-financial.svg"), + ("Wellbeing programs", "Access mental health resources and assistance programs for life's challenges", "benefit-wellbeing.svg"), + ("Paid time off", "Take a break as needed for vacation, sick leave, holidays, parental leave, and more", "benefit-pto.svg"), + ("Career growth opportunities", "Training, leadership programs, and clear paths to advance", "benefit-growth.svg"), + ("Comprehensive health benefits", "Medical, dental, vision, and wellness programs for you and your family", "benefit-health.svg"), +] + +LIFE_AT_WALMART_HEADING = "Life at Walmart" +LIFE_AT_WALMART_QUOTE = ( + "Join us, and help us continue our mission to bring everyday value and support to communities everywhere." +) + +DRUG_FREE_NOTICE = ( + "Walmart is committed to maintaining a drug-free workplace and has a no tolerance policy regarding " + "the use of illegal drugs and alcohol on the job. This policy applies to all employees and aims to " + "create a safe and productive work environment." +) + +HOURLY_PAY_NOTICE = [ + "The actual hourly rate will equal or exceed the required minimum wage applicable to the job location.", + "Additional compensation includes annual or quarterly performance incentives.", + "Additional compensation in the form of premiums may be paid in amounts ranging from $0.35 per hour " + "to $3.00 per hour in specific circumstances. Premiums may be based on schedule, facility, season, " + "or specific work performed. Multiple premiums may apply if applicable criteria are met.", +] + +MIN_QUAL_PREAMBLE = ( + "Outlined below are the required minimum qualifications for this position. If none are listed, " + "there are no minimum qualifications." +) +PREF_QUAL_PREAMBLE = ( + "Outlined below are the optional preferred qualifications for this position. If none are listed, " + "there are no preferred qualifications." +) + +# Salaried detail pages: the boilerplate blocks that follow "What you'll bring" +# on the live corporate postings. Static chrome, keyed by career-area slug for +# the "About ..." paragraph; everything per-posting lives on Job. +SALARIED_ABOUT_AREA = { + "technology": ( + "About Walmart Global Tech", + "Imagine working in an environment where one line of code can make life easier for hundreds of " + "millions of people. That's what we do at Walmart Global Tech. We're a team of software engineers, " + "data scientists, cybersecurity experts and service professionals within the world's leading " + "retailer who make an epic impact and are at the forefront of the next retail disruption. People " + "are why we innovate, and people power our innovations. We are people-led and tech-empowered. We " + "train our team in the skillsets of the future and bring in experts like you to help us grow.", + ), + "corporate": ( + "About Walmart", + "Our home office and corporate teams set the direction for the world's largest retailer: the " + "strategy, the finances, the merchandise, the marketing and the people practices behind more " + "than 10,000 stores and clubs and the associates who run them. The work you do here shows up on " + "shelves and in carts within weeks, not years.", + ), + "students": ( + "About our internships", + "Our internships are paid, project-based and designed to end with a real deliverable. Interns " + "join a team, own a piece of work for the term, present it to leadership and leave with a " + "network across the business. Many of our leaders started as interns.", + ), +} +SALARIED_ABOUT_DEFAULT = ( + "About Walmart", + "Walmart Inc. is the world's largest retailer, serving more than 250 million customers every week " + "through stores, clubs and eCommerce sites in nineteen countries.", +) +SALARIED_HYBRID_NOTE = ( + "We use a hybrid way of working that is primarily in office coupled with virtual when not onsite. " + "Our campuses serve as a hub for collaboration, bring us together for purpose, and deliver on business " + "needs. This approach helps us make quicker decisions, remove location barriers across our global " + "team, and be more flexible in our personal lives." +) +SALARIED_BENEFITS_NOTE = ( + "Beyond our great compensation package, you can receive incentive awards for your performance. Other " + "great perks include 401(k) match, stock purchase plan, paid maternity and parental leave, PTO, " + "multiple health plans, and much more." +) +SALARIED_PAY_NOTE = ( + "At Walmart, we offer competitive pay as well as performance-based bonus awards and other great " + "benefits for a happier mind, body, and wallet. Health benefits include medical, vision and dental " + "coverage. Financial benefits include 401(k), stock purchase and company-paid life insurance. Paid " + "time off benefits include PTO (including sick leave), parental leave, family care leave, " + "bereavement, jury duty, and voting. Other benefits include short-term and long-term disability, " + "company discounts, Military Leave Pay, adoption and surrogacy expense reimbursement, and more." +) +SALARIED_EEO_NOTE = ( + "Walmart, Inc. is an Equal Opportunity Employer - By Choice. We believe we are best equipped to help " + "our associates, customers and the communities we serve live better when we really know them." +) +SALARIED_SCOPE_NOTE = ( + "The above information has been designed to indicate the general nature and level of work performed " + "in the role. It is not designed to contain or be interpreted as a comprehensive inventory of all " + "responsibilities and qualifications required of employees assigned to this job. The full Job " + "Description can be made available as part of the hiring process." +) + +# --------------------------------------------------------------------------- # +# Resources pages +# --------------------------------------------------------------------------- # +LOCATIONS_HEADING = "Our locations" +LOCATIONS_BLURB = ( + "Our hubs spark collaboration and innovation, so you're free to energize and push boundaries " + "from the space that serves you best." +) +LOCATIONS_CLOSING = ( + "Between making an impact at scale and our culture of promoting from within, from coders all the " + "way to cashiers, Walmart is the best place to build a career, period." +) + +HIRING_HEADING = "How we hire" +HIRING_BLURB = ( + "Every career starts with a first step. Whether you're applying for your first job or your next " + "big move, this is the beginning of something new. At Walmart and Sam's Club, the hiring process " + "is about more than landing a role, it's about discovering where you belong, where you can grow, " + "and where your work can make a real difference." +) +HIRING_STEPS = [ + ("1. Find your role", "Search open roles by keyword, career area or location, then save the ones you like."), + ("2. Apply online", "Share your contact details and work history. Most applications take 20-25 minutes."), + ("3. Interview", "A recruiter or hiring manager reaches out, usually within a week of your application."), + ("4. Offer and onboarding", "Accept your offer, complete pre-employment steps and pick your start date."), +] +HIRING_FAQ = [ + ( + "Before you apply", + [ + ( + "Do I need a resume or CV to apply for all Walmart jobs?", + "Not necessarily. A resume or CV is not required to apply, but you will need to provide " + "details about your job history and other information on the application. If you would " + "like to include your resume, LinkedIn profile, portfolio or website, there will be a " + "section where you can add it to your application.", + ), + ( + "How long does it take to fill out an application on average?", + "On average, it takes 20-25 minutes to complete your application for the first time. " + "Subsequent applications will take less time to apply as our system saves your " + "application information.", + ), + ( + "Can I start the application process and finish it later?", + "For hourly roles within the Walmart Online Hiring Center, you have the ability to save " + "your work and log back in at a later time.", + ), + ( + "Can I change my application after submitting?", + "No, you cannot change your application after submitting. Please make sure that " + "everything is finalized before you hit the submit button.", + ), + ], + ), + ( + "After you apply", + [ + ( + "Will I receive confirmation that my application was successfully submitted?", + "Yes. Once you complete your application you will see a confirmation screen with a " + "confirmation number that starts with WMC-.", + ), + ( + "When should I expect to hear back after submitting my application?", + "Timing varies, but we try to respond to applicants within a week of submission.", + ), + ( + "Will I be notified if I am not selected for an interview?", + "Yes, you will be informed if you are not selected for an interview at this time.", + ), + ( + "Do you provide reasonable accommodations during the application process?", + "Yes, reach out to your manager, recruiter or recruiting coordinator about any needs " + "you have. We are happy to do what we can to support you.", + ), + ], + ), +] + +TERMS_HEADING = "Terms & Conditions" +TERMS_SECTIONS = [ + ( + "About this mirror", + "This is an offline WebHarbor mirror of careers.walmart.com built for agent benchmarking. " + "No application submitted here reaches Walmart Inc., and no data leaves the container.", + ), + ( + "Candidate accounts", + "Accounts created on this mirror exist only inside the local database and are removed whenever " + "the environment is reset to its seed state.", + ), + ( + "Applications", + "Submitting an application records a row in the local database and returns a confirmation number " + "in the form WMC-000000. It creates no relationship, express or implied, with Walmart Inc.", + ), + ( + "Accuracy of postings", + "Job postings, pay ranges, store addresses and requisition IDs shown here are synthetic mirror " + "data modelled on the structure of the real site.", + ), +] + +# --------------------------------------------------------------------------- # +# Footer +# --------------------------------------------------------------------------- # +FOOTER_CAREER_LINKS = [ + ("Stores and Clubs", "stores-and-clubs"), + ("Supply Chain and Transportation", "supply-chain-and-transportation"), + ("Healthcare", "healthcare"), + ("Technology", "technology"), + ("Corporate", "corporate"), +] +FOOTER_BRANDS = ["Walmart", "Sam's Club", "VIZIO"] +FOOTER_SOCIAL = [ + ("Facebook", "social-facebook.svg"), + ("Instagram", "social-instagram.svg"), + ("LinkedIn", "social-linkedin.svg"), + ("X", "social-x.svg"), + ("YouTube", "social-youtube.svg"), + ("Glassdoor", "social-glassdoor.svg"), +] +FOOTER_EEO = ( + "Walmart, Inc. is an Equal Opportunity Employer. We believe we are best equipped to help our " + "associates, customers, and the communities we serve live better when we really know them. That " + "means understanding, respecting, and valuing unique styles, experiences, identities, abilities, " + "ideas and opinions- while welcoming all people. Walmart Inc. participates in E-verify. Learn more " + "about applicant rights under Federal Employment Laws." +) +FOOTER_BENEFITS_NOTE = ( + "Eligibility for benefits depends on your job classification, and benefits are subject to specific " + "plan or program terms. For more information about your benefits options, please see the Associate " + "Benefits Book at One.Walmart.com/BenefitsBook." +) + +EMPTY_FUTURE_ROLES = ( + "Future roles are not part of this mirror. Every posting in this environment is an open role you " + "can browse, save and apply to from the Open roles tab." +) +EMPTY_CONTENT_TAB = ( + "Content search is not part of this mirror. Use the Open roles tab, the career area pages or the " + "Resources pages to explore this site." +) + +# --------------------------------------------------------------------------- # +# Coarse lat/lng outlines used by the deterministic server-rendered cluster map. +# Points are (lng, lat). +# --------------------------------------------------------------------------- # +US_OUTLINE = [ + (-124.7, 48.4), (-123.0, 48.2), (-122.6, 47.0), (-124.0, 46.3), (-124.1, 43.7), + (-124.4, 42.0), (-124.2, 40.4), (-122.4, 37.8), (-121.9, 36.6), (-120.6, 34.6), + (-118.4, 33.7), (-117.1, 32.5), (-114.7, 32.7), (-111.1, 31.3), (-108.2, 31.3), + (-106.5, 31.8), (-104.9, 30.6), (-103.1, 29.0), (-101.4, 29.8), (-99.1, 26.4), + (-97.1, 25.9), (-97.4, 28.0), (-95.0, 29.1), (-93.8, 29.7), (-91.0, 29.2), + (-89.4, 29.0), (-89.0, 30.2), (-87.5, 30.3), (-85.0, 29.7), (-84.0, 30.1), + (-82.8, 27.9), (-81.8, 25.9), (-80.1, 25.2), (-80.1, 27.0), (-81.4, 30.7), + (-80.8, 32.0), (-78.9, 33.7), (-75.7, 35.2), (-76.0, 36.9), (-75.1, 38.3), + (-74.0, 39.7), (-73.9, 40.6), (-71.9, 41.3), (-70.0, 41.7), (-70.2, 42.6), + (-70.8, 43.2), (-69.0, 43.9), (-67.0, 44.8), (-67.8, 45.7), (-69.2, 47.5), + (-71.5, 45.0), (-74.7, 45.0), (-76.9, 43.3), (-79.2, 43.4), (-82.4, 41.7), + (-83.1, 42.2), (-82.5, 45.3), (-84.4, 46.5), (-87.6, 46.0), (-88.0, 48.2), + (-89.5, 48.0), (-95.2, 49.0), (-104.0, 49.0), (-116.0, 49.0), (-123.0, 49.0), +] +PR_OUTLINE = [ + (-67.3, 18.5), (-66.4, 18.5), (-65.6, 18.4), (-65.6, 17.9), (-66.6, 17.9), (-67.3, 18.1), +] + + +# --------------------------------------------------------------------------- # +# Header navigation. The dropdown groups mirror the live top bar: +# Career areas | Brands | Resources | About Us | Military. +# --------------------------------------------------------------------------- # +# (label, brand filter value) for the Brands dropdown. +NAV_BRANDS = [ + ("Walmart", "Walmart"), + ("Sam's Club", "Sam's Club"), + ("VIZIO", "Vizio"), +] +# (label, endpoint) for the Resources dropdown. +NAV_RESOURCES = [ + ("How we hire", "resources_hiring"), + ("Office Locations", "resources_location"), + ("Terms & Conditions", "resources_terms"), +] + +ABOUT_HEADING = "About Us" +ABOUT_BLURB = ( + "Walmart is a people-led, tech-powered omnichannel retailer. Around the world our associates " + "serve customers in stores, clubs, distribution centers and online, and every one of those jobs " + "is a step toward something greater." +) +ABOUT_SECTIONS = [ + ( + "Our purpose", + "We save people money so they can live better. That purpose has guided every decision since " + "Sam Walton opened the first store in Rogers, Arkansas, and it still shapes how we hire, how " + "we promote, and how we invest in the communities we serve.", + ), + ( + "How we work", + "We are people-led and tech-powered. Associates in stores, clubs, supply chain and the home " + "office work with the same tools and the same data, so a good idea can start anywhere and " + "reach millions of customers quickly.", + ), + ( + "Where you can grow", + "About three quarters of our salaried store managers began as hourly associates. Live Better U " + "pays for tuition, books and fees, and Walmart Academy runs skills training in every market we " + "operate in.", + ), +] + +# --------------------------------------------------------------------------- # +# US state / territory names, used to resolve a plain state in the location box +# ("PR", "Puerto Rico", "Ohio") into a state-wide result set. +# --------------------------------------------------------------------------- # +STATE_NAMES = { + "AL": "Alabama", "AK": "Alaska", "AZ": "Arizona", "AR": "Arkansas", + "CA": "California", "CO": "Colorado", "CT": "Connecticut", "DC": "District of Columbia", + "DE": "Delaware", "FL": "Florida", "GA": "Georgia", "HI": "Hawaii", + "IA": "Iowa", "ID": "Idaho", "IL": "Illinois", "IN": "Indiana", + "KS": "Kansas", "KY": "Kentucky", "LA": "Louisiana", "MA": "Massachusetts", + "MD": "Maryland", "ME": "Maine", "MI": "Michigan", "MN": "Minnesota", + "MO": "Missouri", "MS": "Mississippi", "MT": "Montana", "NC": "North Carolina", + "ND": "North Dakota", "NE": "Nebraska", "NH": "New Hampshire", "NJ": "New Jersey", + "NM": "New Mexico", "NV": "Nevada", "NY": "New York", "OH": "Ohio", + "OK": "Oklahoma", "OR": "Oregon", "PA": "Pennsylvania", "PR": "Puerto Rico", + "RI": "Rhode Island", "SC": "South Carolina", "SD": "South Dakota", "TN": "Tennessee", + "TX": "Texas", "UT": "Utah", "VA": "Virginia", "VI": "U.S. Virgin Islands", + "VT": "Vermont", "WA": "Washington", "WI": "Wisconsin", "WV": "West Virginia", + "WY": "Wyoming", +} +STATE_CODES_BY_NAME = {name.lower(): code for code, name in sorted(STATE_NAMES.items())} + + +# --------------------------------------------------------------------------- # +# Home page chrome below the fold: the values bento, the milestone badges and +# the "See our associates in action" strip. Static marketing copy only. +# --------------------------------------------------------------------------- # +HOME_INTRO_HEADLINE = ("Grow your future.", "Make an impact.") +HOME_INTRO_CTA = "See our values in action" +BENEFITS_ASIDE = "That's just the beginning. We offer more perks specific to your work location and role." +BENEFITS_CTA = "Learn more about benefits" +MILESTONE_HEADING = "Here, every job is a step toward something greater" +# (figure sentence, badge label, style) — style picks the badge colour scheme. +MILESTONE_BADGES = [ + ("$1 billion invested in associate career training and development", "", "sky"), + ("75% of salaried managers began as hourly associates", "5 YEARS", "spark"), + ("300,000 associates have earned a 10+ year badge", "10 YEARS", "navy"), + ("120,000 U.S. associates have participated in Live Better U", "20 YEARS", "blue"), +] +ASSOCIATES_HEADING = "See our associates in action" +ASSOCIATES_BLURB = ( + "Every day, Walmart associates step up - solving problems, serving communities, and making a " + "difference. They don't just do the job; they bring it to life." +) +FIND_ROLE_HEADING = "Find the role that's a perfect fit" +FIND_ROLE_PLACEHOLDER = "Search by team, department, or keyword" + +# --------------------------------------------------------------------------- # +# Career-area page chrome (per area slug): the lower sections of the L1 pages. +# --------------------------------------------------------------------------- # +AREA_PAGE = { + "stores-and-clubs": { + "tiles": ("Purpose", "Growth", "Pride"), + "headline": "You power the experience for millions", + "cta": "See all stores and clubs roles", + "photos": ("area-stores-3.jpg", "area-stores-2.jpg"), + "quote": "At Walmart and Sam's Club, our stores and clubs are powered by people, dedicated " + "associates working together to create exceptional experiences for the communities " + "we serve.", + "testimonials": [ + ("Curtis", "Store Manager", "Every shift is a chance to make someone's day a little easier."), + ("D'Rogelio", "Store Manager", "You can be you in this environment and still succeed."), + ("Jamaily", "Club Manager", "I started on the floor. Now I run the building."), + ], + }, + "supply-chain-and-transportation": { + "tiles": ("Safety", "Scale", "Momentum"), + "headline": "Move what matters, at scale", + "cta": "See all supply chain roles", + "photos": ("area-supply-chain-2.jpg", "supply-drone.jpg"), + "quote": "Our supply chain associates move millions of items a day through a network that " + "reaches nearly every community in the country - and they do it safely.", + "testimonials": [ + ("Caleb", "Maintenance Tech", "The equipment is the most advanced I've worked on anywhere."), + ("Renee", "Yard Driver", "I know exactly how my work gets product to a shelf."), + ("Marcus", "Area Manager", "We promote from the floor. That's not a slogan here."), + ], + }, + "healthcare": { + "tiles": ("Care", "Community", "Growth"), + "headline": "Care for the communities you call home", + "cta": "See all healthcare roles", + "photos": ("area-healthcare.jpg", "jobhero-wm-2.jpg"), + "quote": "Our pharmacies, vision centers and clinics put affordable care within a short drive " + "of most of the country - and our associates make it personal.", + "testimonials": [ + ("Yasinya", "Pharmacy Tech", "Patients know my name. That's the part I love."), + ("Andre", "Optician", "Every fitting is a small problem to solve well."), + ("Priya", "Pharmacy Manager", "Walmart paid for my certification through Live Better U."), + ], + }, + "technology": { + "tiles": ("Belonging", "Impact"), + "headline": "Tech with real-world impact", + "cta": "See all technology roles", + "photos": ("area-technology-2.jpg", "supply-drone.jpg"), + "quote": "Our vision is strong here. Walmart Global Tech works at the forefront of " + "cutting-edge technologies inspired by the vision of transforming retail tech.", + "hubs_heading": "Three hubs. One mission. Endless possibilities", + "hubs_blurb": "Our hubs spark collaboration and innovation, so you're free to energize and push " + "boundaries from the space that serves you best.", + "testimonials": [ + ("Tatiana", "Software Engineer (iOS)", "The scale of what ships every week still amazes me."), + ("Christopher", "Senior Manager, Food Media Insights", + "We're data geeks, and the depth we get to explore here keeps us excited every single day."), + ("Antony", "Yield Manager", "I get to work on problems no other retailer has."), + ], + }, + "corporate": { + "tiles": ("Curiosity", "Ownership", "Impact"), + "headline": "Shape how the world shops", + "cta": "See all corporate roles", + "photos": ("area-corporate-2.jpg", "jobhero-corp-3.jpg"), + "quote": "From merchandising to finance to people, our home office teams make decisions that " + "reach 240 million customers a week.", + "hubs_heading": "Hubs built for the way you work", + "hubs_blurb": "Bentonville, Sunnyvale, Hoboken and Dallas: pick the space that serves you best.", + "testimonials": [ + ("Jorden", "Associate Merchant", "I own a category. At 26. That doesn't happen elsewhere."), + ("Nina", "Finance Manager", "The numbers are big, but the teams are small and close."), + ("Sam", "People Partner", "We hire for potential and then we invest in it."), + ], + }, + "Military": { + "tiles": ("Transition", "Translate", "Thrive"), + "headline": "Walmart supports Veterans", + "cta": "See all opportunities", + "photos": ("area-military.jpg", "military-banner.png"), + "quote": "Every day, thousands of veterans build careers at Walmart. Learn more about our " + "commitment to veterans and military families.", + "testimonials": [ + ("Mark", "Veteran, Store Coach", "My leadership experience translated on day one."), + ("Kim", "Store Manager", "Walmart's not only committed to the veteran - veteran spouses have just the same opportunity."), + ("Jeremy", "Club Manager", "SkillBridge got me in the door. The team kept me here."), + ], + }, +} +AREA_PAGE_DEFAULT = { + "tiles": ("Purpose", "Growth", "Pride"), + "headline": "Grow your future. Make an impact.", + "cta": "See all open roles", + "photos": ("area-stores-3.jpg", "area-stores-2.jpg"), + "quote": LIFE_AT_WALMART_QUOTE, + "testimonials": [], +} +INSPIRATION_HEADING = "Inspiration in every role" + +# Locations page hero and the promo block on the saved-roles page. +LOCATIONS_HERO_IMAGE = "loc-silicon-valley.jpg" +SAVED_PROMO_HEADLINE = ("Get more out of", "Walmart Careers") +SAVED_PROMO_BLURB = ( + "With an account you get role recommendations, create job alerts, and view your application " + "status from a personalized dashboard." +) +SAVED_PROMO_IMAGE = "area-healthcare.jpg" +SAVED_EMPTY_NOTE = "You have no saved roles." + +# Footer links on the stripped sign-in / register layout. +AUTH_FOOTER_LINKS = [ + "Give feedback", "Terms of Use", "Privacy Notice", "California Supply Chain Act", + "Your Privacy Choices", "Customer Privacy Center", "Notice at Collection", +] +AUTH_COPYRIGHT = "© 2026 Walmart. All Rights Reserved." + + +# --------------------------------------------------------------------------- # +# "Life at Walmart" on the detail pages: a lead sentence, the paragraphs beside +# the photo, the paragraphs under it, the blue quote band and the closing lines. +# Hourly postings use the field copy, salaried postings the home-office copy. +# --------------------------------------------------------------------------- # +LIFE_AT_WALMART_FIELD = { + "lead": "At Walmart, you're welcome for who you are, no matter your background, experiences, " + "or perspectives.", + "left": [ + "Our stores and services are for everyone, and so is our workplace. We believe different " + "experiences drive our ability to better serve our communities and deliver affordable " + "products across the nation.", + "Here, your unique insights and ideas are encouraged, valued, and essential to creating a " + "forward-thinking company that thrives on fresh ideas and dedicated teamwork.", + ], + "right": [], + "band": "Since our founding, we've focused on bringing affordable essentials to families " + "everywhere, and today, Walmart is one of the most recognizable names in retail worldwide.", + "closing": LIFE_AT_WALMART_QUOTE, + "note": "We're driven by a commitment to make life better for millions of customers and support " + "our associates with opportunities to grow, learn, and advance.", + "photo": "jobhero-wm-4.jpg", +} +LIFE_AT_WALMART_CORP = { + "lead": "Imagine a workplace surrounded by innovation. At Walmart's new Home Office in " + "Bentonville, Arkansas, we're redefining what it means to work at a global leader.", + "left": [ + "Set on 350 acres of thoughtfully revitalized land, our new campus seamlessly integrates " + "the charm of Northwest Arkansas with cutting edge design and technology. From biking " + "trails and outdoor courtyards to flexible, tech-enabled workspaces, every detail reflects " + "our commitment to sustainability, connection, and culture.", + ], + "right": [ + "With amenities like on-site childcare at our Little Squiggles Children's Enrichment " + "Center, the Walton Family Whole Health & Fitness Center, and a vibrant food hall " + "featuring local and international favorites, we're creating a space where work-life " + "balance isn't just a goal - it's a reality.", + "Beyond the campus, Bentonville offers a dynamic lifestyle with world-class dining, art at " + "the Crystal Bridges Museum, and countless outdoor activities.", + ], + "band": "Whether you're exploring High South cuisine, enjoying live performances at our new " + "amphitheater, or cycling the Razorback Greenway, you'll experience the perfect blend " + "of small-town charm and big-city amenities.", + "closing": "Join us at Walmart and grow your career alongside a community that feels like home.", + "note": "This isn't just a workplace - it's a destination for leaders eager to make a difference.", + "photo": "life-home-office.jpg", +} + +# Stores & Clubs L1: the "Meet Brandon" day-in-the-life block under the bento. +MEET_STORE_COACH = { + "name": "Brandon", + "kicker": "Day in the life", + "role": "Walmart store coach", + "blurb": "As a store coach, Brandon leads with energy, empathy, and focus. In this video, he " + "shares what it takes to guide a team in one of Walmart's busiest stores - balancing " + "daily priorities, supporting associates, and helping people grow.", +} + +# Military L1: the two feature rows and the three program tiles under the hero. +MILITARY_FEATURES = [ + ( + "SkillBridge: Your Transition, Supported", + "Preparing to separate from active duty? Through the DoD SkillBridge program, you can build " + "career-ready skills with structured training and real-world experience while you're still " + "serving. Explore opportunities designed to help you translate your military strengths into " + "a long-term career at Walmart.", + "Explore opportunities now", + "area-military.jpg", + ), + ( + "Military skills translator", + "Translate your military experience into civilian job skills. Use our tool to discover the " + "best career opportunities that align with your unique qualifications.", + "Explore now", + "jobhero-corp-3.jpg", + ), +] +MILITARY_PROGRAMS = [ + ( + "Discover Walmart: job simulations", + "Experience various roles at Walmart through our flexible job simulations. Choose modules, " + "upskill at your pace, and gain insights to succeed in the application process. Explore " + "multiple career paths at Walmart.", + "jobhero-corp-2.jpg", + ), + ( + "Internships: kickstart your career", + "Our internship programs offer valuable opportunities for individuals at any stage of their " + "education or career. Gain experience in various fields, apply your unique skills, receive " + "mentorship, and get hands-on training that sets you apart in your chosen career path.", + "area-students.png", + ), + ( + "Join our talent network", + "Sign up for our talent network, participate in one of our engaging hiring events or " + "military connected workshops designed to showcase diverse career paths and provide " + "opportunities to network with Walmart professionals.", + "area-corporate.jpg", + ), +] + +# How-we-hire page: hero photo, the intro beside each FAQ group, the job simulator block. +HIRING_HERO_IMAGE = "area-corporate-2.jpg" +HIRING_HERO_CTA = "Explore something new" +HIRING_FAQ_INTROS = [ + ("We're here to help you put your best foot forward. Get tips and guidance to feel confident " + "as you take the first step toward a role that's right for you.", "benefit-wellbeing.svg"), + ("You've taken a big step and we're glad you did! Here's what to expect next, plus answers to " + "common questions to help you stay informed and encouraged along the way.", "benefit-pto.svg"), +] +HIRING_SIMULATOR = { + "heading": "Experience a day in the role", + "blurb": "Want to know what a role at Walmart and Sam's Club is really like? Our interactive job " + "simulations give you a chance to preview the role, showcase your skills, and see if " + "it's a good fit for you.", + "cta": "Job simulator", + "photo": "jobhero-corp-2.jpg", +} + +# Apply flow: the first-party contact step's heading and hint. +APPLY_HEADING = "Let us know how to contact you" +APPLY_EMAIL_HINT = "Avoid using an email address you share with others" diff --git a/sites/walmart_careers/_health.py b/sites/walmart_careers/_health.py new file mode 100644 index 00000000..cd932bcc --- /dev/null +++ b/sites/walmart_careers/_health.py @@ -0,0 +1,5 @@ +"""Per-site health probe (optional, called by control_server).""" + + +def health(): + return {"ok": True, "site": "walmart_careers"} diff --git a/sites/walmart_careers/app.py b/sites/walmart_careers/app.py new file mode 100644 index 00000000..6afe27a9 --- /dev/null +++ b/sites/walmart_careers/app.py @@ -0,0 +1,1172 @@ +"""Walmart Careers local mirror for WebHarbor.""" +from __future__ import annotations + +import json +import math +import os +import re +import sys +from datetime import date, datetime +from pathlib import Path +from urllib.parse import urlencode + +from flask import ( + Flask, + abort, + flash, + jsonify, + redirect, + render_template, + request, + session, + url_for, +) +from flask_login import ( + LoginManager, + UserMixin, + current_user, + login_required, + login_user, + logout_user, +) +from flask_sqlalchemy import SQLAlchemy +from flask_wtf.csrf import CSRFProtect +from werkzeug.security import check_password_hash, generate_password_hash + +import _content as content + +BASE_DIR = Path(__file__).resolve().parent +INSTANCE_DIR = BASE_DIR / "instance" +DB_PATH = INSTANCE_DIR / "walmart_careers.db" + +INSTANCE_DIR.mkdir(parents=True, exist_ok=True) + +app = Flask(__name__, instance_path=str(INSTANCE_DIR)) +app.config["SECRET_KEY"] = "webharbor-walmart-careers-demo-key" +app.config["SQLALCHEMY_DATABASE_URI"] = f"sqlite:///{DB_PATH}" +app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False +app.config["WTF_CSRF_TIME_LIMIT"] = None + +db = SQLAlchemy(app) +csrf = CSRFProtect(app) +login_manager = LoginManager(app) +login_manager.login_view = "login" +login_manager.login_message = "Sign in to continue." +login_manager.login_message_category = "info" + +DEMO_PASSWORD = "TestPass123!" +EMAIL_PATTERN = re.compile(r"^[^@\s]+@[^@\s]+\.[^@\s]+$") +PAGE_SIZE = 10 + +SHIFT_VALUES = [ + "Weekday Day", + "Weekday Evening", + "Weekday Overnight", + "Weekend Day", + "Weekend Evening", + "Weekend Overnight", + "Flex", +] +BRAND_VALUES = ["Vizio", "Walmart", "Sam's Club"] +EMPLOYMENT_TYPE_VALUES = ["Full time", "Part time", "Intern"] +RATE_VALUES = ["Salaried", "Hourly"] +RADIUS_VALUES = [5, 15, 25, 60] +SORT_VALUES = ["relevance", "most_recent"] + +STOPWORDS = { + "a", "an", "and", "at", "for", "in", "of", "on", "or", "the", "to", "with", + "jobs", "job", "roles", "role", "near", "me", "all", +} + + +# --------------------------------------------------------------------------- # +# Models +# --------------------------------------------------------------------------- # +class Area(db.Model): + __tablename__ = "areas" + id = db.Column(db.Integer, primary_key=True) + slug = db.Column(db.String(64), unique=True, nullable=False) + name = db.Column(db.String(80), nullable=False) + display_order = db.Column(db.Integer, nullable=False, default=0) + blurb = db.Column(db.Text, nullable=False, default="") + hero_image = db.Column(db.String(120), nullable=False, default="") + has_index_page = db.Column(db.Boolean, nullable=False, default=True) + is_filterable = db.Column(db.Boolean, nullable=False, default=True) + + categories = db.relationship( + "Category", backref="area", lazy="select", + order_by="Category.display_order", + ) + + @property + def url(self) -> str: + return url_for("career_area", slug=self.slug) + + @property + def nav_url(self) -> str: + """Where the "Career areas" menu points: the area page when there is one, + otherwise straight to that area's open roles.""" + if self.has_index_page: + return url_for("career_area", slug=self.slug) + return url_for("results", area=self.slug) + + +class Category(db.Model): + __tablename__ = "categories" + id = db.Column(db.Integer, primary_key=True) + area_id = db.Column(db.Integer, db.ForeignKey("areas.id"), nullable=False) + name = db.Column(db.String(120), nullable=False) + slug = db.Column(db.String(120), nullable=False) + display_order = db.Column(db.Integer, nullable=False, default=0) + + __table_args__ = (db.UniqueConstraint("area_id", "slug", name="uq_category_area_slug"),) + + +class Store(db.Model): + __tablename__ = "stores" + id = db.Column(db.Integer, primary_key=True) + store_number = db.Column(db.String(16), unique=True, nullable=False) + banner = db.Column(db.String(64), nullable=False) + location_name = db.Column(db.String(120), nullable=False) + street = db.Column(db.String(160), nullable=False) + city = db.Column(db.String(80), nullable=False) + state = db.Column(db.String(2), nullable=False) + zip = db.Column(db.String(12), nullable=False) + lat = db.Column(db.Float, nullable=False) + lng = db.Column(db.Float, nullable=False) + is_hub = db.Column(db.Boolean, nullable=False, default=False) + is_office = db.Column(db.Boolean, nullable=False, default=False) + hub_name = db.Column(db.String(80), nullable=True) + hub_blurb = db.Column(db.Text, nullable=True) + hub_image = db.Column(db.String(80), nullable=True) + + @property + def banner_line(self) -> str: + return f"{self.banner} #{self.store_number}" + + @property + def city_state(self) -> str: + return f"{self.city}, {self.state}" + + +class Job(db.Model): + __tablename__ = "jobs" + job_id = db.Column(db.String(32), primary_key=True) + population = db.Column(db.String(16), nullable=False) # salaried | hourly + title = db.Column(db.String(160), nullable=False) + brand = db.Column(db.String(24), nullable=False) + store_id = db.Column(db.Integer, db.ForeignKey("stores.id"), nullable=False) + area_id = db.Column(db.Integer, db.ForeignKey("areas.id"), nullable=False) + category_id = db.Column(db.Integer, db.ForeignKey("categories.id"), nullable=False) + shifts_json = db.Column(db.Text, nullable=False, default="[]") + employment_type = db.Column(db.String(16), nullable=False) + pay_frequency = db.Column(db.String(8), nullable=False) # Hourly | Annual + min_pay = db.Column(db.Numeric(10, 2), nullable=False) + max_pay = db.Column(db.Numeric(10, 2), nullable=False) + posted_date = db.Column(db.Date, nullable=False) + sort_rank = db.Column(db.Integer, nullable=False, default=0) + is_trending = db.Column(db.Boolean, nullable=False, default=False) + summary = db.Column(db.Text, nullable=False, default="") + description = db.Column(db.Text, nullable=False, default="") + about_team = db.Column(db.Text, nullable=True) # salaried only + additional_description_json = db.Column(db.Text, nullable=True) + hashtag = db.Column(db.String(48), nullable=True) + shift_time = db.Column(db.String(120), nullable=True) + positions_available = db.Column(db.Integer, nullable=True) + min_age_note = db.Column(db.Boolean, nullable=False, default=False) + worker_type = db.Column(db.String(48), nullable=True) + job_posting_id = db.Column(db.String(48), nullable=True) + min_qualifications_json = db.Column(db.Text, nullable=True) + preferred_qualifications = db.Column(db.Text, nullable=True) + hero_images_json = db.Column(db.Text, nullable=False, default="[]") + + store = db.relationship("Store", lazy="joined") + area = db.relationship("Area", lazy="joined") + category = db.relationship("Category", lazy="joined") + + # -- derived display helpers ------------------------------------------- # + @property + def shifts(self) -> list[str]: + return json.loads(self.shifts_json or "[]") + + @property + def shift_label(self) -> str: + values = self.shifts + if len(values) == 1: + return values[0] + return "Multiple shifts" + + @property + def is_salaried(self) -> bool: + return self.population == "salaried" + + @property + def rate_label(self) -> str: + return "Salaried" if self.is_salaried else "Hourly" + + @property + def pay_suffix(self) -> str: + return "/yr" if self.pay_frequency == "Annual" else "/hr" + + @property + def pay_range(self) -> str: + if self.pay_frequency == "Annual": + return f"${int(self.min_pay):,} - ${int(self.max_pay):,}/yr" + return f"${float(self.min_pay):,.2f} - ${float(self.max_pay):,.2f}/hr" + + @property + def additional_description(self) -> list[str]: + return json.loads(self.additional_description_json or "[]") + + @property + def min_qualifications(self) -> list[str]: + return json.loads(self.min_qualifications_json or "[]") + + @property + def hero_images(self) -> list[str]: + return json.loads(self.hero_images_json or "[]") + + @property + def url(self) -> str: + return url_for("job_detail", job_id=self.job_id) + + @property + def search_blob(self) -> str: + """Fields the scored search reads. Description is deliberately excluded.""" + return " ".join( + [ + self.title, + self.category.name if self.category else "", + self.area.name if self.area else "", + self.store.banner if self.store else "", + self.store.city if self.store else "", + self.store.state if self.store else "", + self.brand, + ] + ) + + +class User(UserMixin, db.Model): + __tablename__ = "users" + id = db.Column(db.Integer, primary_key=True) + email = db.Column(db.String(160), unique=True, nullable=False) + username = db.Column(db.String(80), unique=True, nullable=False) + display_name = db.Column(db.String(120), nullable=False, default="") + first_name = db.Column(db.String(80), nullable=False, default="") + last_name = db.Column(db.String(80), nullable=False, default="") + phone = db.Column(db.String(32), nullable=False, default="") + city = db.Column(db.String(80), nullable=False, default="") + state = db.Column(db.String(2), nullable=False, default="") + password_hash = db.Column(db.String(256), nullable=False) + created_at = db.Column(db.DateTime, nullable=False) + + def check_password(self, raw: str) -> bool: + return check_password_hash(self.password_hash, raw) + + +class SavedJob(db.Model): + __tablename__ = "saved_jobs" + id = db.Column(db.Integer, primary_key=True) + user_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False) + job_id = db.Column(db.String(32), db.ForeignKey("jobs.job_id"), nullable=False) + saved_at = db.Column(db.DateTime, nullable=False) + + __table_args__ = (db.UniqueConstraint("user_id", "job_id", name="uq_saved_user_job"),) + + job = db.relationship("Job", lazy="joined") + + +class Application(db.Model): + __tablename__ = "applications" + id = db.Column(db.Integer, primary_key=True) + job_id = db.Column(db.String(32), db.ForeignKey("jobs.job_id"), nullable=False) + user_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=True) + email = db.Column(db.String(160), nullable=False) + first_name = db.Column(db.String(80), nullable=False) + last_name = db.Column(db.String(80), nullable=False) + phone = db.Column(db.String(32), nullable=False) + status = db.Column(db.String(32), nullable=False, default="Submitted") + confirmation_no = db.Column(db.String(32), unique=True, nullable=False) + submitted_at = db.Column(db.DateTime, nullable=False) + + job = db.relationship("Job", lazy="joined") + + +@login_manager.user_loader +def load_user(user_id: str): + return db.session.get(User, int(user_id)) + + +# --------------------------------------------------------------------------- # +# Helpers +# --------------------------------------------------------------------------- # +def dumps_json(value) -> str: + return json.dumps(value, ensure_ascii=False, sort_keys=False, separators=(",", ":")) + + +def confirmation_for(application_id: int) -> str: + return f"WMC-{application_id:06d}" + + +def tokenize(text: str) -> list[str]: + return [t for t in re.split(r"[^a-z0-9']+", (text or "").lower()) if t and t not in STOPWORDS] + + +def haversine_miles(lat1: float, lng1: float, lat2: float, lng2: float) -> float: + radius = 3958.7613 + p1, p2 = math.radians(lat1), math.radians(lat2) + dp = p2 - p1 + dl = math.radians(lng2 - lng1) + a = math.sin(dp / 2) ** 2 + math.cos(p1) * math.cos(p2) * math.sin(dl / 2) ** 2 + return 2 * radius * math.asin(math.sqrt(a)) + + +def safe_next(raw: str | None) -> str | None: + """Only allow same-origin relative paths as a ?next= target.""" + if not raw: + return None + if not raw.startswith("/") or raw.startswith("//") or "\\" in raw: + return None + return raw + + +def resolve_location(raw: str) -> dict | None: + """Resolve free text typed into the location box. + + Returns ``None`` when nothing matches, otherwise a scope dict: + + * ``{"kind": "state", "state": "PR", "label": "Puerto Rico", "store": }`` + when the text names a whole state or territory — the result set is then + every role in that state, with no radius applied. + * ``{"kind": "store", "store": , "label": "Rochester, NY"}`` when the + text names a city, a "City, ST" pair or a ZIP — the radius then applies. + """ + text = (raw or "").strip() + if not text: + return None + stores = Store.query.order_by(Store.store_number).all() + + def state_scope(code: str) -> dict | None: + anchor = next((s for s in stores if s.state == code), None) + if anchor is None: + return None + return { + "kind": "state", + "state": code, + "label": content.STATE_NAMES.get(code, code), + "store": anchor, + } + + # Whole-state searches: "PR", "Puerto Rico", "Ohio". + upper = text.upper() + if len(upper) == 2 and upper in content.STATE_NAMES: + scope = state_scope(upper) + if scope: + return scope + code = content.STATE_CODES_BY_NAME.get(text.lower()) + if code: + scope = state_scope(code) + if scope: + return scope + + digits = re.sub(r"[^0-9]", "", text) + if len(digits) >= 5: + for store in stores: + if store.zip.replace("-", "").startswith(digits[:5]): + return {"kind": "store", "store": store, "label": store.city_state} + parts = [p.strip() for p in text.split(",") if p.strip()] + city = parts[0].lower() if parts else "" + state = parts[1].upper()[:2] if len(parts) > 1 else "" + if state: + for store in stores: + if store.city.lower() == city and store.state == state: + return {"kind": "store", "store": store, "label": store.city_state} + for store in stores: + if store.city.lower() == city: + return {"kind": "store", "store": store, "label": store.city_state} + return None + + +def current_filters() -> dict: + """Read the results-page query string into a normalised dict.""" + args = request.args + query = (args.get("q") or args.get("searchQuery") or "").strip() + if query.lower() == "all": + query = "" + try: + page = max(1, int(args.get("page", "1"))) + except ValueError: + page = 1 + try: + radius = int(args.get("radius", "25")) + except ValueError: + radius = 25 + if radius not in RADIUS_VALUES: + radius = 25 + sort = args.get("sort", "relevance") + if sort not in SORT_VALUES: + sort = "relevance" + return { + "q": query, + "area": [v for v in args.getlist("area") if v], + "category": [v for v in args.getlist("category") if v], + "brand": [v for v in args.getlist("brand") if v in BRAND_VALUES], + "shift": [v for v in args.getlist("shift") if v in SHIFT_VALUES], + "type": [v for v in args.getlist("type") if v in EMPLOYMENT_TYPE_VALUES], + "rate": [v for v in args.getlist("rate") if v in RATE_VALUES], + "loc": (args.get("loc") or "").strip(), + "radius": radius, + "sort": sort, + "page": page, + "tab": args.get("tab", "jobs"), + } + + +def filters_query(filters: dict, **overrides) -> str: + merged = dict(filters) + merged.update(overrides) + pairs: list[tuple[str, str]] = [] + if merged.get("q"): + pairs.append(("q", merged["q"])) + for key in ("area", "category", "brand", "shift", "type", "rate"): + for value in merged.get(key) or []: + pairs.append((key, value)) + if merged.get("loc"): + pairs.append(("loc", merged["loc"])) + pairs.append(("radius", str(merged.get("radius", 25)))) + if merged.get("sort") and merged["sort"] != "relevance": + pairs.append(("sort", merged["sort"])) + if merged.get("tab") and merged["tab"] != "jobs": + pairs.append(("tab", merged["tab"])) + if merged.get("page", 1) and int(merged.get("page", 1)) > 1: + pairs.append(("page", str(merged["page"]))) + return urlencode(pairs) + + +def _stem_match(token: str, blob_tokens: set[str]) -> bool: + """Loose match for inflected forms ('drivers' ~ 'driver', 'stocking' ~ 'stocker'). + + Two words match when they share a prefix of at least six characters and + their lengths are within three of each other. A five-letter prefix was + too loose: 'technician' matched 'Technology' and pulled half the catalog + into a title search. The search is scored, never a strict AND, so this + tier only widens a result set. + """ + if len(token) < 6: + return False + for other in blob_tokens: + if len(other) < 6 or abs(len(other) - len(token)) > 3: + continue + limit = min(len(token), len(other)) + shared = 0 + while shared < limit and token[shared] == other[shared]: + shared += 1 + if shared >= 6: + return True + return False + + +def score_job(job: Job, tokens: list[str]) -> float: + if not tokens: + return 0.0 + blob = job.search_blob.lower() + blob_tokens = {t for t in re.split(r"[^a-z0-9']+", blob) if t} + score = 0.0 + for token in tokens: + if token in blob_tokens: + score += 2.0 + elif any(other.startswith(token) for other in blob_tokens): + # word-prefix tier: 'cashi' -> cashier, 'hand' -> handler. A raw + # substring test let 'care' light up every Healthcare posting. + score += 1.0 + elif _stem_match(token, blob_tokens): + score += 0.5 + title_tokens = {t for t in re.split(r"[^a-z0-9']+", job.title.lower()) if t} + for token in tokens: + if token in title_tokens: + score += 1.5 + return score + + +def search_jobs(filters: dict) -> tuple[list[Job], dict | None, bool]: + """Return (ordered jobs, resolved location scope, location_failed).""" + jobs = Job.query.order_by(Job.job_id).all() + location = None + location_failed = False + if filters["loc"]: + location = resolve_location(filters["loc"]) + location_failed = location is None + + if filters["brand"]: + jobs = [j for j in jobs if j.brand in filters["brand"]] + if filters["type"]: + jobs = [j for j in jobs if j.employment_type in filters["type"]] + if filters["rate"]: + wanted = {"Salaried": "salaried", "Hourly": "hourly"} + allowed = {wanted[r] for r in filters["rate"]} + jobs = [j for j in jobs if j.population in allowed] + if filters["shift"]: + wanted_shifts = set(filters["shift"]) + jobs = [j for j in jobs if wanted_shifts & set(j.shifts)] + if filters["area"]: + wanted_areas = {a.lower() for a in filters["area"]} + jobs = [ + j for j in jobs + if j.area and (j.area.slug.lower() in wanted_areas or j.area.name.lower() in wanted_areas) + ] + if filters["category"]: + wanted_cats = {c.lower() for c in filters["category"]} + jobs = [ + j for j in jobs + if j.category and (j.category.slug.lower() in wanted_cats or j.category.name.lower() in wanted_cats) + ] + if location is not None: + if location["kind"] == "state": + jobs = [j for j in jobs if j.store.state == location["state"]] + else: + anchor = location["store"] + radius = filters["radius"] + jobs = [ + j for j in jobs + if haversine_miles(anchor.lat, anchor.lng, j.store.lat, j.store.lng) <= radius + ] + + tokens = tokenize(filters["q"]) + if tokens: + scored = [(score_job(j, tokens), j) for j in jobs] + scored = [(s, j) for s, j in scored if s > 0] + if filters["sort"] == "most_recent": + scored.sort(key=lambda pair: (-pair[1].posted_date.toordinal(), pair[1].sort_rank)) + else: + scored.sort(key=lambda pair: (-pair[0], pair[1].sort_rank, pair[1].job_id)) + jobs = [j for _, j in scored] + else: + if filters["sort"] == "most_recent": + jobs.sort(key=lambda j: (-j.posted_date.toordinal(), j.sort_rank)) + else: + jobs.sort(key=lambda j: (j.sort_rank, j.job_id)) + return jobs, location, location_failed + + +def cluster_map_svg(jobs: list[Job], width: int = 520, height: int = 620) -> str: + """Deterministic server-rendered cluster map (no third-party map tiles). + + Equirectangular with a cos(mean latitude) correction so the outline keeps a + believable shape, then centred vertically in the panel. + """ + lon_min, lon_max = -125.0, -65.0 + lat_min, lat_max = 17.0, 50.0 + pad = 12 + scale = (width - 2 * pad) / (lon_max - lon_min) + lat_scale = scale / math.cos(math.radians((lat_min + lat_max) / 2)) + y_offset = (height - (lat_max - lat_min) * lat_scale) / 2 + + def project(lat: float, lng: float) -> tuple[float, float]: + x = pad + (lng - lon_min) * scale + y = y_offset + (lat_max - lat) * lat_scale + return round(x, 1), round(y, 1) + + def path_for(points: list[tuple[float, float]]) -> str: + coords = [project(lat, lng) for lng, lat in points] + head = f"M {coords[0][0]} {coords[0][1]}" + rest = " ".join(f"L {x} {y}" for x, y in coords[1:]) + return f"{head} {rest} Z" + + counts: dict[int, int] = {} + for job in jobs: + counts[job.store_id] = counts.get(job.store_id, 0) + 1 + bubbles = [] + for store_id, count in sorted(counts.items(), key=lambda kv: (-kv[1], kv[0])): + store = db.session.get(Store, store_id) + if store is None: + continue + x, y = project(store.lat, store.lng) + radius = 12 + min(14, count * 2) + bubbles.append((x, y, radius, count, f"{store.city}, {store.state}")) + + parts = [ + f'', + f'', + f'', + f'', + ] + for x, y, radius, count, label in bubbles: + parts.append( + f'{label}: {count} open roles' + f'' + f'{count}' + ) + parts.append( + f'Map data ©2026 Walmart Careers mirror' + ) + parts.append("") + return "".join(parts) + + +def pin_card_svg(store: Store, width: int = 490, height: int = 230) -> str: + """Small deterministic SVG map card used beside the address on the detail page. + + Stands in for the Google Maps thumbnail on the live page: same palette, a road + grid seeded from the store's own coordinates, and a pin over the location. + """ + seed = int(abs(store.lat * 1000) + abs(store.lng * 1000)) % 97 + vx = 40 + (seed % 7) * 22 + vy = 60 + (seed % 5) * 18 + parts = [ + f'', + f'', + # water + f'', + # roads + f'', + f'', + f'', + f'', + ] + px, py = width / 2, height / 2 - 18 + parts.append( + f'' + f'' + f'' + f'' + ) + parts.append( + f'' + f'{store.city}' + ) + parts.append( + f'' + f'Map data ©2026 Walmart Careers mirror' + ) + parts.append("") + return "".join(parts) + + +def trending_jobs() -> list[Job]: + return Job.query.filter_by(is_trending=True).order_by(Job.job_id).all() + + +def related_jobs(job: Job, limit: int = 3) -> list[Job]: + rows = ( + Job.query.filter( + Job.category_id == job.category_id, + Job.job_id != job.job_id, + Job.store_id != job.store_id, + ) + .order_by(Job.sort_rank, Job.job_id) + .limit(limit) + .all() + ) + if len(rows) < limit: + extra = ( + Job.query.filter( + Job.area_id == job.area_id, + Job.job_id != job.job_id, + Job.store_id != job.store_id, + ~Job.job_id.in_([r.job_id for r in rows]), + ) + .order_by(Job.sort_rank, Job.job_id) + .limit(limit - len(rows)) + .all() + ) + rows = rows + extra + return rows + + +def saved_job_ids() -> set[str]: + if not current_user.is_authenticated: + return set() + return { + row.job_id + for row in SavedJob.query.filter_by(user_id=current_user.id).all() + } + + +@app.context_processor +def inject_globals(): + return { + # the six career areas listed in the header "Career areas" menu + "nav_areas": ( + Area.query.filter_by(is_filterable=True) + .order_by(Area.display_order) + .all() + ), + "content": content, + "current_year": content.MIRROR_REFERENCE_DATE.year, + "search_q": (request.args.get("q") or request.args.get("searchQuery") or ""), + } + + +# --------------------------------------------------------------------------- # +# Routes +# --------------------------------------------------------------------------- # +@app.route("/home") +@app.route("/") +def index(): + areas = ( + Area.query.filter_by(has_index_page=True, is_filterable=True) + .order_by(Area.display_order) + .all() + ) + return render_template( + "index.html", + trending=trending_jobs(), + ribbon_areas=areas, + saved_ids=saved_job_ids(), + ) + + +FACET_KEYS = ("area", "category", "brand", "shift", "type", "rate") + + +def active_filter_count(filters: dict) -> int: + """How many facet selections are active — the number on the Filters button.""" + return sum(len(filters[key]) for key in FACET_KEYS) + + +def active_filter_chips(filters: dict) -> list[dict]: + """One removable chip per active selection, so the current filter state stays + readable without leaving the Filters popover hanging open over the results.""" + names = {a.slug: a.name for a in Area.query.all()} + names.update({c.slug: c.name for c in Category.query.all()}) + chips: list[dict] = [] + for key in FACET_KEYS: + for value in filters[key]: + remaining = [v for v in filters[key] if v != value] + chips.append( + { + "label": names.get(value, value), + "remove": url_for("results") + + "?" + + filters_query(filters, page=1, **{key: remaining}), + } + ) + if filters["loc"]: + chips.append( + { + "label": f"{filters['loc']} · within {filters['radius']} miles", + "remove": url_for("results") + "?" + filters_query(filters, loc="", page=1), + } + ) + return chips + + +@app.route("/results") +def results(): + filters = current_filters() + jobs, location, location_failed = search_jobs(filters) + total = len(jobs) + pages = max(1, math.ceil(total / PAGE_SIZE)) + page = min(filters["page"], pages) + filters["page"] = page + start = (page - 1) * PAGE_SIZE + page_jobs = jobs[start:start + PAGE_SIZE] + + areas = Area.query.filter_by(is_filterable=True).order_by(Area.display_order).all() + return render_template( + "results.html", + filters=filters, + jobs=page_jobs, + total=total, + page=page, + pages=pages, + areas=areas, + shift_values=SHIFT_VALUES, + brand_values=BRAND_VALUES, + employment_type_values=EMPLOYMENT_TYPE_VALUES, + rate_values=RATE_VALUES, + radius_values=RADIUS_VALUES, + location=location, + location_failed=location_failed, + map_svg=cluster_map_svg(jobs), + saved_ids=saved_job_ids(), + qs=filters_query, + filter_count=active_filter_count(filters), + filter_chips=active_filter_chips(filters), + ) + + +@app.route("/jobs/") +def job_detail(job_id: str): + job = db.session.get(Job, job_id) + if job is None: + abort(404) + return render_template( + "job_detail.html", + job=job, + related=related_jobs(job), + is_saved=job.job_id in saved_job_ids(), + map_svg=pin_card_svg(job.store), + benefit_tiles=content.benefit_tiles_for(job.brand, job.population), + saved_ids=saved_job_ids(), + ) + + +@app.route("/jobs//save", methods=["POST"]) +def save_job(job_id: str): + job = db.session.get(Job, job_id) + if job is None: + abort(404) + if not current_user.is_authenticated: + return redirect(url_for("login", next=url_for("job_detail", job_id=job_id))) + existing = SavedJob.query.filter_by(user_id=current_user.id, job_id=job_id).first() + if existing is None: + db.session.add( + SavedJob(user_id=current_user.id, job_id=job_id, saved_at=datetime.now()) + ) + db.session.commit() + flash(f"Saved {job.title} to your saved roles.", "success") + target = safe_next(request.form.get("next")) or url_for("job_detail", job_id=job_id) + return redirect(target) + + +@app.route("/jobs//unsave", methods=["POST"]) +def unsave_job(job_id: str): + job = db.session.get(Job, job_id) + if job is None: + abort(404) + if not current_user.is_authenticated: + return redirect(url_for("login", next=url_for("saved_roles"))) + existing = SavedJob.query.filter_by(user_id=current_user.id, job_id=job_id).first() + if existing is not None: + db.session.delete(existing) + db.session.commit() + flash(f"Removed {job.title} from your saved roles.", "success") + target = safe_next(request.form.get("next")) or url_for("saved_roles") + return redirect(target) + + +@app.route("/jobs//apply", methods=["GET", "POST"]) +def apply_contact(job_id: str): + job = db.session.get(Job, job_id) + if job is None: + abort(404) + errors: list[str] = [] + form = { + "email": "", + "first_name": "", + "last_name": "", + "phone": "", + } + if current_user.is_authenticated: + form.update( + { + "email": current_user.email, + "first_name": current_user.first_name, + "last_name": current_user.last_name, + "phone": current_user.phone, + } + ) + if request.method == "POST": + for key in form: + form[key] = (request.form.get(key) or "").strip() + agreed = request.form.get("terms") == "on" + if not EMAIL_PATTERN.fullmatch(form["email"]): + errors.append("Enter a valid email address.") + if not form["first_name"]: + errors.append("Enter your first name.") + if not form["last_name"]: + errors.append("Enter your last name.") + if len(re.sub(r"[^0-9]", "", form["phone"])) < 10: + errors.append("Enter a phone number with at least 10 digits.") + if not agreed: + errors.append("You must accept the Terms & Conditions to continue.") + if not errors: + session["apply_draft"] = {"job_id": job_id, **form} + return redirect(url_for("apply_confirm", job_id=job_id)) + return render_template("apply_contact.html", job=job, form=form, errors=errors) + + +@app.route("/jobs//apply/confirm", methods=["GET", "POST"]) +def apply_confirm(job_id: str): + job = db.session.get(Job, job_id) + if job is None: + abort(404) + draft = session.get("apply_draft") + if not draft or draft.get("job_id") != job_id: + flash("Start your application by entering your contact details.", "warning") + return redirect(url_for("apply_contact", job_id=job_id)) + if request.method == "POST": + application = Application( + job_id=job_id, + user_id=current_user.id if current_user.is_authenticated else None, + email=draft["email"], + first_name=draft["first_name"], + last_name=draft["last_name"], + phone=draft["phone"], + status="Submitted", + confirmation_no="pending", + submitted_at=datetime.now(), + ) + db.session.add(application) + db.session.flush() + application.confirmation_no = confirmation_for(application.id) + db.session.commit() + session.pop("apply_draft", None) + session["apply_submitted_id"] = application.id + return redirect(url_for("apply_submitted", job_id=job_id)) + return render_template("apply_confirm.html", job=job, draft=draft) + + +@app.route("/jobs//apply/submitted") +def apply_submitted(job_id: str): + job = db.session.get(Job, job_id) + if job is None: + abort(404) + application_id = session.get("apply_submitted_id") + application = db.session.get(Application, application_id) if application_id else None + if application is None or application.job_id != job_id: + flash("We couldn't find that application. Please apply again.", "warning") + return redirect(url_for("apply_contact", job_id=job_id)) + return render_template("apply_submitted.html", job=job, application=application) + + +@app.route("/careers-areas/") +def career_area(slug: str): + area = Area.query.filter(db.func.lower(Area.slug) == slug.lower()).first() + if area is None: + abort(404) + if not area.has_index_page: + # Students has no index page upstream either; send it to its open roles. + return redirect(url_for("results", area=area.slug)) + categories = ( + Category.query.filter_by(area_id=area.id) + .order_by(Category.display_order) + .all() + ) + return render_template( + "area.html", + area=area, + categories=categories, + hubs=Store.query.filter_by(is_hub=True).order_by(Store.id).all(), + ) + + +@app.route("/resources/location") +def resources_location(): + hubs = Store.query.filter_by(is_hub=True).order_by(Store.id).all() + return render_template("locations.html", hubs=hubs) + + +@app.route("/resources/hiring-process") +def resources_hiring(): + return render_template("hiring_process.html", trending=trending_jobs(), saved_ids=saved_job_ids()) + + +@app.route("/resources/terms-and-conditions") +def resources_terms(): + return render_template("terms.html") + + +@app.route("/about-us") +def about_us(): + return render_template( + "about.html", + areas=Area.query.filter_by(is_filterable=True).order_by(Area.display_order).all(), + ) + + +@app.route("/login", methods=["GET", "POST"]) +def login(): + next_url = safe_next(request.args.get("next")) + errors: list[str] = [] + email = "" + if request.method == "POST": + email = (request.form.get("email") or "").strip().lower() + password = request.form.get("password") or "" + next_url = safe_next(request.form.get("next")) or next_url + user = User.query.filter_by(email=email).first() + if user is None or not user.check_password(password): + errors.append("We couldn't sign you in with that email and password.") + else: + login_user(user) + flash(f"Signed in as {user.display_name}.", "success") + return redirect(next_url or url_for("index")) + return render_template("login.html", errors=errors, email=email, next_url=next_url) + + +@app.route("/register", methods=["GET", "POST"]) +def register(): + next_url = safe_next(request.args.get("next")) + errors: list[str] = [] + form = {"email": "", "first_name": "", "last_name": ""} + if request.method == "POST": + for key in form: + form[key] = (request.form.get(key) or "").strip() + password = request.form.get("password") or "" + confirm = request.form.get("confirm_password") or "" + next_url = safe_next(request.form.get("next")) or next_url + email = form["email"].lower() + if not EMAIL_PATTERN.fullmatch(email): + errors.append("Enter a valid email address.") + elif User.query.filter_by(email=email).first(): + errors.append("An account already exists for that email address.") + if len(password) < 8: + errors.append("Choose a password with at least 8 characters.") + if password != confirm: + errors.append("The two passwords don't match.") + if not form["first_name"]: + errors.append("Enter your first name.") + if not form["last_name"]: + errors.append("Enter your last name.") + if not errors: + display = f"{form['first_name']} {form['last_name']}".strip() + base_username = email.split("@")[0] + username = base_username + suffix = 2 + while User.query.filter_by(username=username).first(): + username = f"{base_username}.{suffix}" + suffix += 1 + user = User( + email=email, + username=username, + display_name=display, + first_name=form["first_name"], + last_name=form["last_name"], + phone="", + city="", + state="", + password_hash=generate_password_hash(password), + created_at=datetime.now(), + ) + db.session.add(user) + db.session.commit() + login_user(user) + flash("Your candidate account is ready.", "success") + return redirect(next_url or url_for("saved_roles")) + return render_template("register.html", errors=errors, form=form, next_url=next_url) + + +@app.route("/logout", methods=["POST"]) +@login_required +def logout(): + logout_user() + session.pop("apply_draft", None) + session.pop("apply_submitted_id", None) + flash("You have been signed out.", "info") + return redirect(url_for("index")) + + +@app.route("/account") +@login_required +def account(): + return render_template( + "account.html", + saved_count=SavedJob.query.filter_by(user_id=current_user.id).count(), + application_count=Application.query.filter_by(user_id=current_user.id).count(), + ) + + +@app.route("/account/edit", methods=["GET", "POST"]) +@login_required +def account_edit(): + errors: list[str] = [] + form = { + "display_name": current_user.display_name, + "first_name": current_user.first_name, + "last_name": current_user.last_name, + "phone": current_user.phone, + "city": current_user.city, + "state": current_user.state, + } + if request.method == "POST": + for key in form: + form[key] = (request.form.get(key) or "").strip() + form["state"] = form["state"].upper()[:2] + if not form["display_name"]: + errors.append("Enter a display name.") + if form["state"] and not re.fullmatch(r"[A-Z]{2}", form["state"]): + errors.append("Use a two-letter state code.") + if not errors: + for key, value in form.items(): + setattr(current_user, key, value) + db.session.commit() + flash("Your profile has been updated.", "success") + return redirect(url_for("account")) + return render_template("account_edit.html", form=form, errors=errors) + + +@app.route("/candidate-home/saved-roles") +def saved_roles(): + rows: list[SavedJob] = [] + if current_user.is_authenticated: + rows = ( + SavedJob.query.filter_by(user_id=current_user.id) + .order_by(SavedJob.saved_at.desc(), SavedJob.id.desc()) + .all() + ) + return render_template( + "saved_roles.html", + rows=rows, + trending=trending_jobs(), + saved_ids=saved_job_ids(), + ) + + +@app.route("/candidate-home/applications") +@login_required +def applications(): + rows = ( + Application.query.filter_by(user_id=current_user.id) + .order_by(Application.submitted_at.desc(), Application.id.desc()) + .all() + ) + return render_template("applications.html", rows=rows) + + +@app.route("/_health") +def health(): + return jsonify( + { + "ok": True, + "site": "walmart_careers", + "jobs": Job.query.count(), + "stores": Store.query.count(), + "areas": Area.query.count(), + "categories": Category.query.count(), + "users": User.query.count(), + } + ) + + +@app.errorhandler(404) +def not_found(_error): + return render_template("404.html"), 404 + + +@app.errorhandler(500) +def server_error(_error): # pragma: no cover - defensive + return render_template("404.html"), 500 + + +def bootstrap_site() -> None: + from seed_data import seed_benchmark_users, seed_database + + with app.app_context(): + db.create_all() + seed_database() + seed_benchmark_users() + + +# `python app.py` loads this file as __main__; register it under its import +# name too so seed_data's `from app import ...` reuses this module instead of +# building a second Flask app + SQLAlchemy instance. +sys.modules.setdefault("app", sys.modules[__name__]) + +if os.environ.get("WEBSYN_SKIP_BOOTSTRAP") != "1": + bootstrap_site() + + +if __name__ == "__main__": + port = int(os.environ.get("PORT", "5000")) + app.run(host="0.0.0.0", port=port, debug=False) diff --git a/sites/walmart_careers/catalog_source.py b/sites/walmart_careers/catalog_source.py new file mode 100644 index 00000000..6cbb4134 --- /dev/null +++ b/sites/walmart_careers/catalog_source.py @@ -0,0 +1,2147 @@ +"""Deterministic source catalog for the Walmart Careers mirror. + +Every posting in the mirror is generated from this file: title families with an +explicit list of placements (store, employment type, shifts, pay band, open +positions). Body copy comes from per-family templates with slot fills, so the +200 postings stay internally consistent without 200 hand-written essays. + +Nothing here is read at request time. `seed_data.py` turns it into SQLite rows. +""" +from __future__ import annotations + +# --------------------------------------------------------------------------- # +# Areas +# --------------------------------------------------------------------------- # +# (slug, name, display_order, blurb, hero_image, has_index_page, is_filterable) +AREAS = [ + ( + "stores-and-clubs", + "Stores and Clubs", + 1, + "Find your path with us. Whether you're interested in auto care, front-end services, " + "general merchandising, or another team, you'll find opportunities to grow and the " + "support to reach your career goals.", + "area-stores.jpg", + True, + True, + ), + ( + "supply-chain-and-transportation", + "Supply Chain and Transportation", + 2, + "Move product, move people forward. Our distribution centers, fulfillment centers and " + "private fleet keep shelves stocked and orders on time across the country.", + "area-supply-chain.jpg", + True, + True, + ), + ( + "healthcare", + "Healthcare", + 3, + "Care that reaches everyone. Our pharmacy, vision and wellness teams serve millions of " + "neighbours every week, right inside the stores they already shop.", + "area-healthcare.jpg", + True, + True, + ), + ( + "technology", + "Technology", + 4, + "At Walmart and Sam's Club, we are people-led, tech-powered. Everything you build here - " + "from smarter supply chains to seamless shopping - starts with real needs and creates " + "real impact.", + "area-technology.jpg", + True, + True, + ), + ( + "corporate", + "Corporate", + 5, + "Strategy, finance, merchandising, marketing and people teams that set the direction for " + "the world's largest retailer.", + "area-corporate.jpg", + True, + True, + ), + ( + "students", + "Students", + 6, + "Internships and early career programs across every part of the business.", + "area-students.png", + False, + True, + ), + ( + "Military", + "Military", + 7, + "Your service prepared you to lead. Bring that experience to a company that hires " + "thousands of veterans, transitioning service members and military spouses every year.", + "area-military.jpg", + True, + False, + ), +] + +# (area_slug, category_name, category_slug, display_order) +CATEGORIES = [ + ("stores-and-clubs", "Cashier and Front-End Services", "cashier-and-front-end-services", 1), + ("stores-and-clubs", "Food and Grocery", "food-and-grocery", 2), + ("stores-and-clubs", "General Merchandise, Stocking, and Unloading", "general-merchandise-stocking-and-unloading", 3), + ("stores-and-clubs", "Digital Pickup and Delivery", "digital-pickup-and-delivery", 4), + ("stores-and-clubs", "Cafe", "cafe", 5), + ("stores-and-clubs", "Retail Management", "retail-management", 6), + ("stores-and-clubs", "Fuel Station", "fuel-station", 7), + ("stores-and-clubs", "Auto Care Center", "auto-care-center", 8), + ("stores-and-clubs", "Auto Services", "auto-services", 9), + ("stores-and-clubs", "Maintenance", "maintenance", 10), + ("stores-and-clubs", "Security and Asset Protection", "security-and-asset-protection", 11), + ("supply-chain-and-transportation", "SC&T Operations", "sct-operations", 1), + ("supply-chain-and-transportation", "Drivers", "drivers", 2), + ("supply-chain-and-transportation", "Engineering", "engineering", 3), + ("supply-chain-and-transportation", "Aviation", "aviation", 4), + ("supply-chain-and-transportation", "Security and Asset Protection", "sct-security-and-asset-protection", 5), + ("healthcare", "Pharmacy Services", "pharmacy-services", 1), + ("healthcare", "Optical Services", "optical-services", 2), + ("healthcare", "Health and Wellness Operations", "health-and-wellness-operations", 3), + ("healthcare", "Clinical Care", "clinical-care", 4), + ("technology", "Software Engineering and Architecture", "software-engineering-and-architecture", 1), + ("technology", "Product Management", "product-management", 2), + ("technology", "Data Science and Analytics", "data-science-and-analytics", 3), + ("technology", "Information Security", "information-security", 4), + ("technology", "Creative Design and UX", "creative-design-and-ux", 5), + ("technology", "Technical Program Management", "technical-program-management", 6), + ("technology", "Information Technology", "information-technology", 7), + ("corporate", "Accounting and Finance", "accounting-and-finance", 1), + ("corporate", "Human Resources", "human-resources", 2), + ("corporate", "Marketing and Advertising", "marketing-and-advertising", 3), + ("corporate", "Merchandising", "merchandising", 4), + ("corporate", "Business Operations", "business-operations", 5), + ("students", "Internship", "internship", 1), +] + +# --------------------------------------------------------------------------- # +# Stores (store_number, banner, location_name, street, city, state, zip, +# lat, lng, is_hub, is_office, brand) +# --------------------------------------------------------------------------- # +STORES = [ + # --- offices ----------------------------------------------------------- + ("10101", "Home Office", "WALMART HOME OFFICE", "702 SW 8th St", "Bentonville", "AR", "72716-0000", 36.363430, -94.219970, True, True, "Walmart"), + ("11807", "Home Office", "SUNNYVALE TECH CORNERS BLDG 6", "811 11th Ave", "Sunnyvale", "CA", "94089-4731", 37.402869, -122.036132, True, True, "Walmart"), + ("11003", "Home Office", "HOBOKEN TECH HUB", "221 River St", "Hoboken", "NJ", "07030-5989", 40.735657, -74.030324, True, True, "Walmart"), + ("11500", "Home Office", "DALLAS METRO OFFICE", "603 Munger Ave", "Dallas", "TX", "75202-3505", 32.784618, -96.796851, True, True, "Walmart"), + ("11109", "Home Office", "SAM'S CLUB HOME OFFICE", "2101 SE Simple Savings Dr", "Bentonville", "AR", "72712-4304", 36.343100, -94.196000, False, True, "Sam's Club"), + ("12200", "Vizio Campus", "VIZIO IRVINE CAMPUS", "39 Tesla", "Irvine", "CA", "92618-4603", 33.650800, -117.744400, False, True, "Vizio"), + # --- Arkansas ---------------------------------------------------------- + ("5260", "Neighborhood Market", "WM NEIGHBORHOOD MARKET #5260", "1400 SE Walton Blvd", "Bentonville", "AR", "72712-6220", 36.354900, -94.202500, False, False, "Walmart"), + ("144", "WM Supercenter", "WM SUPERCENTER #144", "2110 W Walnut St", "Rogers", "AR", "72756-3611", 36.334100, -94.152800, False, False, "Walmart"), + ("8259", "Sam's Club", "SAM'S CLUB #8259", "1101 SE Walton Blvd", "Bentonville", "AR", "72712-6191", 36.357800, -94.199200, False, False, "Sam's Club"), + ("8155", "Sam's Club", "SAM'S CLUB #8155", "3081 N College Ave", "Fayetteville", "AR", "72703-5100", 36.101000, -94.158000, False, False, "Sam's Club"), + # --- California -------------------------------------------------------- + ("9054", "eComm Whse Logistics", "ECOMM WHSE LOGISTICS #9054", "1290 W Henderson Ave", "Porterville", "CA", "93257-5969", 36.070300, -119.041800, False, False, "Walmart"), + ("2050", "WM Supercenter", "WM SUPERCENTER #2050", "3680 W Shaw Ave", "Fresno", "CA", "93711-3204", 36.808900, -119.828600, False, False, "Walmart"), + ("6608", "Sam's Club", "SAM'S CLUB #6608", "5205 Monterey Hwy", "San Jose", "CA", "95111-4106", 37.259700, -121.816200, False, False, "Sam's Club"), + # --- New Jersey -------------------------------------------------------- + ("2110", "WM Supercenter", "WM SUPERCENTER #2110", "400 Park Plaza Dr", "Secaucus", "NJ", "07094-3661", 40.786600, -74.061800, False, False, "Walmart"), + ("3520", "Neighborhood Market", "WM NEIGHBORHOOD MARKET #3520", "2100 88th St", "North Bergen", "NJ", "07047-4720", 40.792400, -74.011200, False, False, "Walmart"), + # --- Texas ------------------------------------------------------------- + ("9399", "eComm Whse Logistics", "ECOMM WHSE LOGISTICS #9399", "3401 Quincy St", "Plainview", "TX", "79072-3308", 34.164300, -101.700900, False, False, "Walmart"), + ("4750", "Sam's Club", "SAM'S CLUB #4750", "3000 E Plano Pkwy", "Plano", "TX", "75074-7440", 33.017200, -96.671900, False, False, "Sam's Club"), + ("471", "WM Supercenter", "WM SUPERCENTER #471", "4215 Canyon Dr", "Amarillo", "TX", "79110-1109", 35.166900, -101.850700, False, False, "Walmart"), + ("3826", "Neighborhood Market", "WM NEIGHBORHOOD MARKET #3826", "1521 N Cockrell Hill Rd", "Dallas", "TX", "75211-7407", 32.779000, -96.887000, False, False, "Walmart"), + # --- Florida ----------------------------------------------------------- + ("3387", "WM Supercenter", "WM SUPERCENTER #3387", "17000 Toledo Blade Blvd", "North Port", "FL", "34287-7281", 27.056300, -82.183200, False, False, "Walmart"), + ("6318", "Sam's Club", "SAM'S CLUB #6318", "4763 Millenia Plaza Way", "Orlando", "FL", "32839-6014", 28.485600, -81.430200, False, False, "Sam's Club"), + ("7133", "Regional DC", "REGIONAL DISTRIBUTION CENTER #7133", "3001 Bartow Rd", "Lakeland", "FL", "33803-6413", 27.981100, -81.930100, False, False, "Walmart"), + # --- Ohio -------------------------------------------------------------- + ("2073", "WM Supercenter", "WM SUPERCENTER #2073", "10000 Brookpark Rd", "Cleveland", "OH", "44130-1102", 41.409300, -81.786600, False, False, "Walmart"), + ("5388", "Neighborhood Market", "WM NEIGHBORHOOD MARKET #5388", "6594 Ridge Rd", "Parma", "OH", "44129-5546", 41.387000, -81.748000, False, False, "Walmart"), + ("6636", "Sam's Club", "SAM'S CLUB #6636", "3950 W Dublin Granville Rd", "Columbus", "OH", "43235-2701", 40.098700, -83.083100, False, False, "Sam's Club"), + ("5439", "Neighborhood Market", "WM NEIGHBORHOOD MARKET #5439", "5821 W Central Ave", "Toledo", "OH", "43615-2159", 41.673900, -83.673400, False, False, "Walmart"), + ("2075", "WM Supercenter", "WM SUPERCENTER #2075", "8585 Pearl Rd", "Strongsville", "OH", "44136-1618", 41.314000, -81.829000, False, False, "Walmart"), + ("5133", "WM Supercenter", "WM SUPERCENTER #5133", "24801 Brookpark Rd", "North Olmsted", "OH", "44070-3407", 41.429000, -81.916000, False, False, "Walmart"), + ("4744", "Sam's Club", "SAM'S CLUB #4744", "3560 Steelyard Dr", "Cleveland", "OH", "44109-2101", 41.458600, -81.688900, False, False, "Sam's Club"), + # --- New York ---------------------------------------------------------- + ("9046", "eComm Whse Logistics", "ECOMM WHSE LOGISTICS #9046", "8827 Old River Rd", "Marcy", "NY", "13403-3030", 43.173965, -75.315183, False, False, "Walmart"), + ("6038", "Regional DC", "REGIONAL DISTRIBUTION CENTER #6038", "5000 Halsey Rd", "Marcy", "NY", "13403-2317", 43.155900, -75.297400, False, False, "Walmart"), + ("2163", "WM Supercenter", "WM SUPERCENTER #2163", "1490 Hudson Ave", "Rochester", "NY", "14621-2404", 43.201800, -77.586300, False, False, "Walmart"), + # --- Kansas ------------------------------------------------------------ + ("5991", "Neighborhood Market", "WM NEIGHBORHOOD MARKET #5991", "2441 S Rock Rd", "Wichita", "KS", "67207-3254", 37.653900, -97.240100, False, False, "Walmart"), + ("1179", "WM Supercenter", "WM SUPERCENTER #1179", "1301 SW Wanamaker Rd", "Topeka", "KS", "66604-3843", 39.032200, -95.762700, False, False, "Walmart"), + ("6014", "Regional DC", "REGIONAL DISTRIBUTION CENTER #6014", "2101 S Princeton St", "Ottawa", "KS", "66067-8501", 38.588600, -95.263700, False, False, "Walmart"), + # --- Mississippi ------------------------------------------------------- + ("954", "WM Supercenter", "WM SUPERCENTER #954", "1266 Highway 51 N", "Hazlehurst", "MS", "39083-2217", 31.879400, -90.397300, False, False, "Walmart"), + ("1230", "WM Supercenter", "WM SUPERCENTER #1230", "1130 Brookway Blvd", "Brookhaven", "MS", "39601-3211", 31.556600, -90.443800, False, False, "Walmart"), + ("8253", "Sam's Club", "SAM'S CLUB #8253", "6360 Ridgewood Ct Dr", "Jackson", "MS", "39211-3520", 32.393200, -90.140800, False, False, "Sam's Club"), + # --- Iowa -------------------------------------------------------------- + ("9281", "eComm Whse Logistics", "ECOMM WHSE LOGISTICS #9281", "2600 Iris Rd", "Mount Pleasant", "IA", "52641-3106", 40.966400, -91.549600, False, False, "Walmart"), + ("1236", "WM Supercenter", "WM SUPERCENTER #1236", "5101 SE 14th St", "Des Moines", "IA", "50320-2201", 41.531700, -93.596800, False, False, "Walmart"), + # --- Washington -------------------------------------------------------- + ("4137", "WM Supercenter", "WM SUPERCENTER #4137", "1965 S Union Ave", "Tacoma", "WA", "98405-1615", 47.242300, -122.484500, False, False, "Walmart"), + ("6216", "Sam's Club", "SAM'S CLUB #6216", "9950 N Newport Hwy", "Spokane", "WA", "99218-1240", 47.741400, -117.400600, False, False, "Sam's Club"), + ("5382", "Neighborhood Market", "WM NEIGHBORHOOD MARKET #5382", "8102 Evergreen Way", "Everett", "WA", "98203-6428", 47.905400, -122.229900, False, False, "Walmart"), + ("7021", "Regional DC", "REGIONAL DISTRIBUTION CENTER #7021", "1300 Wine Country Rd", "Grandview", "WA", "98930-9704", 46.254000, -119.901000, False, False, "Walmart"), + # --- Puerto Rico ------------------------------------------------------- + ("2503", "WM Supercenter", "WM SUPERCENTER #2503", "Carr 2 KM 11.4", "Bayamon", "PR", "00959-5100", 18.394200, -66.155300, False, False, "Walmart"), + ("2610", "WM Supercenter", "WM SUPERCENTER #2610", "500 Ave Rafael Cordero", "Caguas", "PR", "00725-3607", 18.245600, -66.036200, False, False, "Walmart"), + ("3512", "Neighborhood Market", "WM NEIGHBORHOOD MARKET #3512", "2000 Ave Las Americas", "Ponce", "PR", "00717-0777", 18.019800, -66.612600, False, False, "Walmart"), + ("8763", "Sam's Club", "SAM'S CLUB #8763", "100 Ave Fragoso", "Carolina", "PR", "00979-1234", 18.417400, -65.977300, False, False, "Sam's Club"), + ("3593", "Neighborhood Market", "WM NEIGHBORHOOD MARKET #3593", "65 Ave De Diego", "San Juan", "PR", "00927-3300", 18.398500, -66.055300, False, False, "Walmart"), + # --- Virginia ---------------------------------------------------------- + ("1399", "WM Supercenter", "WM SUPERCENTER #1399", "1123 E Lynchburg Salem Tpke", "Bedford", "VA", "24523-3446", 37.323200, -79.502400, False, False, "Walmart"), + ("6088", "Import", "IMPORT DISTRIBUTION CENTER #6088", "8109 Merrimac Trail", "Williamsburg", "VA", "23185-6255", 37.288600, -76.664900, False, False, "Walmart"), +] + +# --------------------------------------------------------------------------- # +# Shifts +# --------------------------------------------------------------------------- # +SHIFT_CODES = { + "WD": "Weekday Day", + "WE": "Weekday Evening", + "WN": "Weekday Overnight", + "SD": "Weekend Day", + "SE": "Weekend Evening", + "SN": "Weekend Overnight", + "FX": "Flex", +} +SHIFT_WINDOWS = { + "WD": "Shift may start between 6:00am - 11:00am", + "WE": "Shift may start between 12:00pm - 5:00pm", + "WN": "Shift may start between 8:00pm - 1:00am", + "SD": "Shift may start between 5:00am - 10:00am", + "SE": "Shift may start between 1:00pm - 6:00pm", + "SN": "Shift may start between 6:00pm - 3:00am", + "FX": "Shift may start between 7:00am - 7:00pm", +} + + +# --------------------------------------------------------------------------- # +# Hourly title families. +# +# placement tuple: (store_number, employment_type, shift_codes, min_pay, +# max_pay, positions_available, extras) +# `extras` is an optional dict: {"job_id": ..., "shift_time": ..., "min_age": bool} +# Body copy is a per-family template; slots are {banner} {store} {city} {state}. +# --------------------------------------------------------------------------- # +HOURLY_FAMILIES = [ + { + "title": "Freight Handler", + "area": "supply-chain-and-transportation", + "category": "SC&T Operations", + "hashtag": "#freighthandlerjobs", + "summary": "Career opportunities in Freight Handling roles include Receiving, Unloading, " + "Processing, Orderfilling and Shipping.", + "do": [ + "As a Freight Handler at {banner} #{store} in {city}, {state}, you will have a critical role " + "in moving product through our supply chain network to the stores that serve our customers. " + "Your role is critical in providing our customers with the product they expect at an everyday " + "low price.", + "You can expect the work to be very physically demanding with an extremely high focus on your " + "safety and the safety of others. You will be lifting heavy cases in a climate-controlled and, " + "at times, non-climate-controlled environment. The flow of freight is very fast-paced and " + "productivity expectations are high.", + ], + "bring": [ + "Unload, sort and stage inbound freight using powered industrial equipment after certification.", + "Scan and verify case counts against the trailer manifest and flag discrepancies to the area coach.", + "Maintain a clean and safe work area, following all lockout/tagout and PPE requirements.", + "Complies with company policies, procedures, and standards of ethics and integrity. Performs " + "additional duties as assigned.", + ], + "placements": [ + ("9054", "Full time", "SN", 21.80, 25.30, 3, {"shift_time": "Shift may start between 6:00pm - 3:00am"}), + ("9399", "Part time", "SE", 18.50, 22.00, 2, None), + ("9046", "Full time", "WN", 20.90, 24.40, 4, {"shift_time": "Shift may start between 9:00pm - 1:30am"}), + ("6038", "Full time", "WE", 19.75, 23.25, 2, {"shift_time": "Shift may start between 3:00pm - 7:30pm"}), + ("9281", "Part time", "WD", 18.90, 22.40, 3, None), + ("6014", "Part time", "SD", 19.40, 22.90, 5, None), + ("7021", "Full time", "WN", 20.50, 24.00, 3, None), + ], + }, + { + "title": "eCom Warehouse Worker", + "area": "supply-chain-and-transportation", + "category": "SC&T Operations", + "hashtag": "#ecomwarehousejobs", + "summary": "Pick, pack and ship the online orders that customers are waiting on, inside one of " + "our fulfillment buildings.", + "do": [ + "As an eCom Warehouse Worker at {banner} #{store} in {city}, {state}, you pick customer orders " + "from bins and totes, pack them to our quality standard, and hand them to the outbound dock so " + "they ship the same night.", + "Expect a fast, metrics-driven floor. You will stand and walk for most of your shift, lift up to " + "50 pounds, and rotate across pick, pack and ship stations as volume moves.", + ], + "bring": [ + "Pick and pack customer orders to the published units-per-hour standard.", + "Use a handheld scanner and the warehouse management system to confirm every unit.", + "Report damaged product and inventory discrepancies before the order leaves the building.", + "Complies with company policies, procedures, and standards of ethics and integrity.", + ], + "placements": [ + ("9046", "Part time", "SN", 21.35, 24.85, 2, {"job_id": "CP-9046-11274", "shift_time": "Shift may start between 6:00pm - 2:30am"}), + ("9054", "Part time", "WN", 20.60, 24.10, 1, None), + ("9281", "Part time", "SD,WN", 19.20, 22.70, 3, None), + ("7133", "Full time", "WD", 18.80, 22.30, 4, None), + ], + }, + { + "title": "Order Filler", + "area": "supply-chain-and-transportation", + "category": "SC&T Operations", + "hashtag": "#orderfillerjobs", + "summary": "Build store-ready pallets from the pick line and stage them for the outbound fleet.", + "do": [ + "Order Fillers at {banner} #{store} in {city}, {state} select cases from the pick line, build " + "stable pallets to the store's plan-o-gram sequence, wrap them, and stage them at the outbound " + "door for the driver.", + "You will use a rider pallet jack and a voice-directed pick system. Accuracy targets and case " + "rates are published daily and reviewed with your coach each week.", + ], + "bring": [ + "Select cases accurately using a voice-directed picking headset.", + "Build and wrap pallets that travel safely without shifting.", + "Operate a rider pallet jack after completing on-site certification.", + "Complies with company policies, procedures, and standards of ethics and integrity.", + ], + "placements": [ + ("6014", "Full time", "WN,FX,SN", 19.10, 22.60, 4, None), + ("7133", "Part time", "SE,FX", 18.40, 21.90, 2, None), + ("6038", "Part time", "WD,FX", 20.10, 23.60, 3, None), + ("9399", "Full time", "SN,FX", 19.85, 23.35, 2, None), + ("7021", "Part time", "SD,FX", 19.30, 22.80, 2, None), + ], + }, + { + "title": "Yard Driver-Off Property", + "area": "supply-chain-and-transportation", + "category": "Drivers", + "hashtag": "#yarddriverjobs", + "summary": "Move trailers between the yard, the dock doors and nearby off-property lots.", + "do": [ + "Yard Drivers at {banner} #{store} in {city}, {state} shuttle trailers between dock doors, the " + "on-site yard and nearby off-property parking so that inbound and outbound freight never waits " + "on a door.", + "You will spend the shift in a yard tractor, outdoors in all weather, coordinating over radio " + "with the dock office and the guard shack.", + ], + "bring": [ + "Hold a valid Class A commercial driver's license with a clean motor vehicle record.", + "Spot and pull trailers safely in tight yard conditions, day or night.", + "Complete yard checks and record trailer locations in the yard management system.", + "Complies with company policies, procedures, and standards of ethics and integrity.", + ], + "placements": [ + ("6088", "Part time", "SE", 22.25, 25.75, 2, None), + ("7133", "Part time", "WD", 23.10, 26.60, 1, None), + ("6014", "Full time", "WN", 22.80, 26.30, 3, None), + ("9399", "Part time", "FX,SN", 21.90, 25.40, 2, None), + ], + }, + { + "title": "Class A CDL Truck Driver", + "area": "supply-chain-and-transportation", + "category": "Drivers", + "hashtag": "#drivewithwalmart", + "summary": "Run scheduled store deliveries out of a private fleet transportation office.", + "do": [ + "Drivers based at {banner} #{store} in {city}, {state} run scheduled routes to stores and clubs " + "in the surrounding region, unloading with the store team and returning with backhaul freight.", + "Our private fleet runs newer equipment, publishes routes in advance and gets most drivers home " + "regularly. Safety scorecards are reviewed with your transportation manager every month.", + ], + "bring": [ + "Hold a valid Class A CDL and meet all federal Department of Transportation requirements.", + "At least 30 months of experience in the last 4 years driving a tractor trailer.", + "No preventable accidents or serious traffic violations in the last three years.", + "Complies with company policies, procedures, and standards of ethics and integrity.", + ], + "placements": [ + ("6038", "Full time", "WD", 32.00, 41.00, 6, None), + ("6014", "Full time", "SD,WN", 31.50, 40.50, 4, None), + ("7133", "Full time", "WE", 30.75, 39.75, 5, None), + ("6088", "Full time", "WN", 33.25, 42.25, 3, None), + ], + }, + { + "title": "Asset Protection Associate - All DC/FC", + "area": "supply-chain-and-transportation", + "category": "Security and Asset Protection", + "hashtag": "#dcassetprotectionjobs", + "summary": "Protect people, product and property inside a distribution or fulfillment building.", + "do": [ + "Asset Protection Associates at {banner} #{store} in {city}, {state} control access at the guard " + "shack and associate entrances, audit trailer seals, and run the camera system that covers the " + "dock and the yard.", + "You will partner with operations leadership on safety walks, investigate shrink incidents, and " + "write up findings for the asset protection manager.", + ], + "bring": [ + "Control access to the building and the yard, verifying credentials at every entry point.", + "Audit inbound and outbound trailer seals against the manifest.", + "Monitor camera systems and document incidents accurately and promptly.", + "Complies with company policies, procedures, and standards of ethics and integrity.", + ], + "placements": [ + ("9046", "Part time", "WD,FX", 22.00, 25.50, 2, None), + ("9054", "Part time", "SD,FX", 21.00, 24.50, 1, None), + ("6088", "Full time", "WN", 22.50, 26.00, 2, None), + ("7133", "Full time", "SN,FX", 23.00, 26.50, 1, None), + ("6014", "Part time", "WE", 20.75, 24.25, 3, None), + ], + }, + { + "title": "Facility Maintenance Technician", + "area": "supply-chain-and-transportation", + "category": "Engineering", + "hashtag": "#facilitymaintenancejobs", + "summary": "Keep conveyors, dock equipment and building systems running across the shift.", + "do": [ + "Facility Maintenance Technicians at {banner} #{store} in {city}, {state} perform preventive " + "maintenance and emergency repairs on conveyor systems, sortation equipment, dock levellers and " + "building services.", + "You will read schematics, troubleshoot electrical and mechanical faults, and close out work " + "orders in the maintenance system before the end of your shift.", + ], + "bring": [ + "Two years of industrial maintenance experience or a completed technical program.", + "Troubleshoot 480V three-phase systems, motor controls and pneumatics safely.", + "Read and work from electrical, mechanical and pneumatic schematics.", + "Complies with company policies, procedures, and standards of ethics and integrity.", + ], + "placements": [ + ("9046", "Full time", "WD,FX", 26.50, 34.00, 2, None), + ("9281", "Full time", "WN,FX", 25.75, 33.25, 1, None), + ("6038", "Full time", "SD", 27.00, 34.50, 2, None), + ("7133", "Full time", "WE,FX", 26.00, 33.50, 3, None), + ("6014", "Part time", "FX", 25.25, 32.75, 1, None), + ("7021", "Full time", "WD,FX", 26.25, 33.75, 1, None), + ], + }, + { + "title": "Automation Technician", + "area": "supply-chain-and-transportation", + "category": "Engineering", + "hashtag": "#automationtechjobs", + "summary": "Support the robotics and controls that run our automated storage and retrieval systems.", + "do": [ + "Automation Technicians at {banner} #{store} in {city}, {state} maintain the robotics cells, " + "programmable controllers and vision systems behind our automated storage and retrieval " + "operation.", + "You will run diagnostics from the controls HMI, replace failed drives and sensors, and escalate " + "recurring faults to the controls engineering team with the data to back it up.", + ], + "bring": [ + "Experience maintaining PLC-controlled equipment, servo drives and industrial networks.", + "Comfort working at height and in confined maintenance aisles under lockout/tagout.", + "Track fault history and parts usage in the maintenance management system.", + "Complies with company policies, procedures, and standards of ethics and integrity.", + ], + "placements": [ + ("9054", "Full time", "WN", 28.00, 36.00, 2, None), + ("9399", "Full time", "SN", 27.50, 35.50, 1, None), + ("6088", "Full time", "WD", 29.00, 37.00, 2, None), + ], + }, + { + "title": "Aviation Line Service Technician", + "area": "supply-chain-and-transportation", + "category": "Aviation", + "hashtag": "#aviationjobs", + "summary": "Fuel, tow and service company aircraft on the ramp at a fleet operations base.", + "do": [ + "Line Service Technicians supporting {banner} #{store} in {city}, {state} marshal, fuel, tow and " + "de-ice company aircraft, and keep the ramp and hangar to airfield standard.", + "You will work directly with flight crews and the maintenance team, following company and FAA " + "ground handling procedures on every movement.", + ], + "bring": [ + "Ramp, fueling or ground handling experience at a fixed base operator or airline.", + "Valid driver's license and the ability to obtain an airport security badge.", + "Careful documentation of every fuel load and aircraft movement.", + "Complies with company policies, procedures, and standards of ethics and integrity.", + ], + "placements": [ + ("6014", "Full time", "WD", 24.00, 31.00, 1, None), + ("7133", "Part time", "SD", 23.50, 30.50, 1, None), + ("6088", "Part time", "WE", 22.75, 29.75, 2, None), + ("6038", "Full time", "FX,SN", 24.50, 31.50, 1, None), + ], + }, + { + "title": "Inventory Control Clerk", + "area": "supply-chain-and-transportation", + "category": "SC&T Operations", + "hashtag": "#inventorycontroljobs", + "summary": "Own cycle counts, research and the paperwork that keeps building inventory accurate.", + "do": [ + "Inventory Control Clerks at {banner} #{store} in {city}, {state} run daily cycle counts, " + "research variances between the system and the slot, and correct records so the building ships " + "what the store ordered.", + "Provides clerical and administrative support through generating and maintaining forms, reports " + "and logs via computerized management software, and communicating with the operations team on " + "open research.", + ], + "bring": [ + "Clerical duties (filing, keying, faxing), entering and extracting data from multiple systems.", + "Use of computer applications required (email, spreadsheets, word processing, and Microsoft Office).", + "The ability to be accurate and focus on attention to details will be critical.", + "Complies with company policies, procedures, and standards of ethics and integrity.", + ], + "placements": [ + ("9281", "Part time", "WD,FX", 19.00, 22.50, 2, None), + ("9399", "Part time", "WE,FX", 19.60, 23.10, 1, None), + ("9046", "Full time", "SE,FX", 20.25, 23.75, 2, None), + ], + }, + # ----------------------------- Stores and Clubs ------------------------ + { + "title": "Cashier & Front End Services", + "area": "stores-and-clubs", + "category": "Cashier and Front-End Services", + "hashtag": "#frontendservicesjobs", + "summary": "Greet members and customers at the front end, ring transactions and keep lines moving.", + "do": [ + "At {banner} #{store} in {city}, {state} you are the last person a customer sees, so you set the " + "tone for the whole trip. You ring up orders quickly and accurately, bag with care, and answer " + "questions about returns, pickup and our app.", + "You will rotate across registers, self checkout and the service desk depending on the hour, and " + "you will be on your feet for most of the shift.", + ], + "bring": [ + "Ring transactions accurately and handle cash, cards and digital tenders.", + "Support self checkout, resolving item and payment issues for customers.", + "Process returns and exchanges at the service desk to policy.", + "Complies with company policies, procedures, and standards of ethics and integrity.", + ], + "placements": [ + ("2503", "Part time", "WD,SD", 15.00, 24.00, 5, None), + ("2610", "Full time", "WD,WE,SD", 15.00, 24.00, 3, None), + ("3512", "Part time", "SE,SN", 15.00, 23.00, 4, None), + ("8763", "Part time", "SE,SN", 16.00, 25.00, 2, None), + ("2110", "Full time", "WD,WE", 15.50, 26.00, 4, None), + ("5382", "Part time", "WE,SE", 17.00, 28.00, 3, None), + ("3593", "Part time", "WE,SN", 15.00, 23.50, 4, None), + ("2075", "Part time", "WE,SE", 15.50, 25.50, 2, None), + ("3826", "Part time", "WE,SE", 15.00, 24.50, 2, None), + ], + }, + { + "title": "Cosmetics Cashier", + "area": "stores-and-clubs", + "category": "Cashier and Front-End Services", + "hashtag": "#cosmeticscashierjobs", + "summary": "Run the beauty counter register and keep the cosmetics department shoppable.", + "do": [ + "The cosmetics counter at {banner} #{store} in {city}, {state} has its own register and its own " + "regulars. You ring transactions there, help customers find shades and brands, and keep the " + "planogram faced and stocked.", + "You will also handle the department's security cases and coordinate restock with the general " + "merchandise team.", + ], + "bring": [ + "Ring transactions at a departmental register and reconcile the till at shift end.", + "Keep the beauty planogram faced, stocked and free of expired product.", + "Open locked cases for customers and follow high-theft merchandise procedures.", + "Complies with company policies, procedures, and standards of ethics and integrity.", + ], + "placements": [ + ("2503", "Part time", "WD,WE", 15.00, 24.00, 1, None), + ("3520", "Part time", "SD,SE", 15.00, 24.00, 2, None), + ("2163", "Part time", "WE,SE", 15.00, 25.00, 1, None), + ], + }, + { + "title": "Member Services Associate", + "area": "stores-and-clubs", + "category": "Cashier and Front-End Services", + "hashtag": "#memberservicesjobs", + "summary": "Sign up new members, renew memberships and solve problems at the member services desk.", + "do": [ + "At {banner} #{store} in {city}, {state} you own the member services desk: new sign-ups, " + "renewals, upgrades, returns and the occasional tough conversation.", + "You will explain plan tiers and instant savings honestly, resolve billing questions, and hand " + "off anything you cannot fix to the club lead with the full picture.", + ], + "bring": [ + "Enroll, renew and upgrade memberships accurately in the membership system.", + "Process returns and refunds within club policy.", + "Explain plan benefits clearly without overselling.", + "Complies with company policies, procedures, and standards of ethics and integrity.", + ], + "placements": [ + ("8259", "Full time", "WD,SD", 18.00, 26.00, 2, None), + ("6318", "Part time", "WE,SE", 16.00, 24.00, 3, None), + ("8763", "Full time", "WD,WE", 16.00, 24.00, 1, None), + ("4744", "Part time", "WE,SE", 16.50, 24.50, 3, None), + ("8155", "Full time", "WD,SD", 17.00, 25.00, 2, None), + ], + }, + { + "title": "Food & Grocery Associate", + "area": "stores-and-clubs", + "category": "Food and Grocery", + "hashtag": "#foodandgroceryjobs", + "summary": "Stock, rotate and merchandise the grocery aisles, coolers and freezers.", + "do": [ + "Food & Grocery Associates at {banner} #{store} in {city}, {state} unload the grocery truck, " + "stock dry, chilled and frozen departments, rotate dated product, and zone the aisles so the " + "store is shoppable at open.", + "You will handle food safety checks on your section and pull anything past code before it " + "reaches a customer.", + ], + "bring": [ + "Stock and rotate product following first-in, first-out standards.", + "Complete temperature and date checks for chilled and frozen sections.", + "Operate a manual and electric pallet jack after certification.", + "Complies with company policies, procedures, and standards of ethics and integrity.", + ], + "placements": [ + ("2110", "Full time", "WD,WE", 15.50, 26.00, 3, None), + ("2050", "Part time", "WE,SE", 17.50, 28.00, 2, None), + ("471", "Full time", "WD,SD", 15.00, 25.00, 4, None), + ("1236", "Part time", "WN,SN", 16.00, 26.00, 2, {"job_id": "CP-1236-10741"}), + ("5388", "Full time", "WN", 16.50, 27.00, 2, None), + ("5133", "Full time", "WE,SN", 16.00, 26.50, 2, None), + ], + }, + { + "title": "Freezer/Cooler Associate", + "area": "stores-and-clubs", + "category": "Food and Grocery", + "hashtag": "#freezercoolerjobs", + "summary": "Work the club's freezer and cooler boxes, stocking bulk frozen and chilled product.", + "do": [ + "At {banner} #{store} in {city}, {state} you spend most of the shift inside the freezer and " + "cooler boxes, breaking down pallets of bulk frozen and chilled product and building the club " + "floor displays.", + "Cold weather gear is provided. You will follow scheduled warm-up breaks and log box " + "temperatures every rotation.", + ], + "bring": [ + "Work extended periods in temperatures as low as minus ten degrees Fahrenheit.", + "Break down pallets and build club floor displays to the merchandising plan.", + "Record freezer and cooler temperatures on the posted schedule.", + "Complies with company policies, procedures, and standards of ethics and integrity.", + ], + "placements": [ + ("6608", "Full time", "WD,WN", 19.00, 27.00, 1, None), + ("4750", "Part time", "SN,WN", 17.50, 25.50, 2, None), + ("8253", "Full time", "WN,SN", 17.00, 25.00, 1, None), + ("8155", "Part time", "WN,SN", 17.50, 25.50, 2, None), + ], + }, + { + "title": "General Merchandise Associate", + "area": "stores-and-clubs", + "category": "General Merchandise, Stocking, and Unloading", + "hashtag": "#generalmerchandisejobs", + "summary": "Unload, sort and stock general merchandise across the sales floor.", + "do": [ + "General Merchandise Associates at {banner} #{store} in {city}, {state} unload trailers, sort " + "freight to department, stock shelves and pull back the overhead so the floor stays full.", + "You will work modular resets, price changes and seasonal transitions alongside the department " + "team lead.", + ], + "bring": [ + "Unload and sort freight accurately to department.", + "Stock, zone and face assigned departments to company standard.", + "Execute modular resets and seasonal transitions on schedule.", + "Complies with company policies, procedures, and standards of ethics and integrity.", + ], + "placements": [ + ("2050", "Part time", "WN,SN", 17.50, 28.00, 3, None), + ("3387", "Part time", "WD,SD", 15.00, 25.00, 2, None), + ("1179", "Full time", "WE,SE,WN", 15.00, 25.00, 4, None), + ("954", "Part time", "WD,WE", 15.00, 24.00, 2, None), + ("5133", "Part time", "SD,SE", 15.50, 25.50, 3, None), + ], + }, + { + "title": "Stocking Associate", + "area": "stores-and-clubs", + "category": "General Merchandise, Stocking, and Unloading", + "hashtag": "#stockingassociatejobs", + "summary": "Work the overnight stocking team, filling the store before the doors open.", + "do": [ + "Stocking Associates at {banner} #{store} in {city}, {state} work the truck overnight: unload, " + "sort to aisle, stock, break down cardboard and zone the floor so the store looks new at open.", + "The pace is set by the truck. You will move steadily for the whole shift with a small team and " + "a clear finish line.", + ], + "bring": [ + "Stock assigned aisles completely and accurately before the store opens.", + "Break down and bale cardboard, keeping aisles clear and safe.", + "Use a manual pallet jack and rolltainers safely in tight aisles.", + "Complies with company policies, procedures, and standards of ethics and integrity.", + ], + "placements": [ + ("1230", "Full time", "WN", 15.50, 24.50, 3, None), + ("2163", "Part time", "SN,WN", 16.50, 26.50, 2, None), + ("2075", "Full time", "WD,SD", 16.00, 25.00, 3, None), + ("3593", "Part time", "WN", 15.00, 24.00, 2, None), + ], + }, + { + "title": "Merchandising and Stocking Associate", + "area": "stores-and-clubs", + "category": "General Merchandise, Stocking, and Unloading", + "hashtag": "#merchandisingjobs", + "summary": "Build club pallets and keep the sales floor merchandised to plan.", + "do": [ + "At {banner} #{store} in {city}, {state} you stock bulk club pallets, build feature displays at " + "the action alley, and keep signage and pricing accurate across your zone.", + "You will use an electric pallet jack and order picker after certification, and you will work " + "with the merchandising lead on weekly display changes.", + ], + "bring": [ + "Stock club pallets and build feature displays to the merchandising plan.", + "Verify signage and pricing against the weekly plan every shift.", + "Operate an electric pallet jack and order picker after certification.", + "Complies with company policies, procedures, and standards of ethics and integrity.", + ], + "placements": [ + ("4750", "Part time", "SN,WN", 17.00, 19.50, 3, None), + ("6318", "Part time", "SN,SD", 21.00, 24.50, 2, None), + ("6636", "Full time", "SN,WN", 18.00, 19.75, 4, None), + ("8253", "Part time", "SN,WE", 17.50, 19.25, 2, None), + ("6216", "Part time", "WN,WD", 18.50, 19.90, 3, None), + ("8763", "Part time", "SN,SE", 16.50, 18.75, 1, None), + ], + }, + { + "title": "Online Order Filling Team Associate", + "area": "stores-and-clubs", + "category": "Digital Pickup and Delivery", + "hashtag": "#onlineorderfillingjobs", + "summary": "Shop, stage and hand off customer pickup and delivery orders.", + "do": [ + "Online Order Filling Team Associates at {banner} #{store} in {city}, {state} shop customer " + "orders from the sales floor with a cart and a handheld, choose the freshest substitutions when " + "an item is out, and stage completed orders in the pickup coolers.", + "You will also load orders into customer vehicles at the pickup canopy and hand off to delivery " + "drivers on schedule.", + ], + "bring": [ + "Pick customer orders accurately against the handheld pick list.", + "Choose quality substitutions and communicate them to the customer.", + "Stage orders at the correct temperature and load them at the pickup canopy.", + "Complies with company policies, procedures, and standards of ethics and integrity.", + ], + "placements": [ + ("1399", "Part time", "WD,WE,SD", 14.50, 27.50, 3, None), + ("144", "Full time", "WD,SD", 15.50, 26.00, 2, None), + ("5991", "Part time", "WE,SE", 17.50, 28.00, 4, None), + ("5260", "Full time", "WD,WE,SE", 15.00, 28.00, 2, None), + ("5388", "Part time", "WE,SE", 16.00, 27.00, 2, None), + ("2075", "Full time", "WD,WE", 15.50, 26.50, 3, None), + ("3826", "Full time", "WD,SD", 15.00, 26.00, 2, None), + ("3593", "Full time", "WD,SE", 14.50, 25.00, 4, None), + ], + }, + { + "title": "Online Order Filling Team Supervisor", + "area": "stores-and-clubs", + "category": "Digital Pickup and Delivery", + "hashtag": "#digitalpickupleadjobs", + "summary": "Lead the pickup and delivery team through the day's order volume.", + "do": [ + "The Online Order Filling Team Supervisor at {banner} #{store} in {city}, {state} runs the " + "digital team for the shift: assigns pickers, watches the order clock, and steps in wherever " + "the queue is tightest.", + "You will coach on pick quality and substitution decisions, handle escalated customer issues at " + "the canopy, and report on-time performance to the store lead each day.", + ], + "bring": [ + "Assign and balance pick work across the team through peak windows.", + "Coach associates on pick accuracy, substitutions and customer handoff.", + "Resolve escalated pickup and delivery issues at the canopy.", + "Complies with company policies, procedures, and standards of ethics and integrity.", + ], + "placements": [ + ("2073", "Full time", "WD,SD", 20.00, 33.00, 2, None), + ("1179", "Full time", "WD,WE", 19.50, 32.00, 1, None), + ("5439", "Part time", "WE,SE", 19.00, 31.50, 2, None), + ], + }, + { + "title": "Cafe Associate", + "area": "stores-and-clubs", + "category": "Cafe", + "hashtag": "#cafeassociatejobs", + "summary": "Run the club cafe: prep, grill, serve and keep the counter to food safety standard.", + "do": [ + "Cafe Associates at {banner} #{store} in {city}, {state} take orders, prep and cook to the " + "posted recipe cards, and keep the counter, the drink station and the seating area clean " + "through the rush.", + "You will run opening or closing food safety checklists and log temperatures on every batch.", + ], + "bring": [ + "Prepare food to recipe and hold it at safe temperatures.", + "Complete opening, mid-shift and closing food safety logs.", + "Keep the counter, equipment and seating area clean and stocked.", + "Complies with company policies, procedures, and standards of ethics and integrity.", + ], + "placements": [ + ("6608", "Part time", "WD,WE", 17.00, 24.00, 2, None), + ("8259", "Part time", "WD,SD", 16.00, 23.00, 1, None), + ("6318", "Part time", "SD,SE,FX", 15.50, 22.50, 3, None), + ("6636", "Part time", "WD,SD", 16.00, 23.00, 2, None), + ("4744", "Full time", "WD,SD", 16.50, 23.50, 2, None), + ], + }, + { + "title": "Team Lead", + "area": "stores-and-clubs", + "category": "Retail Management", + "hashtag": "#teamleadjobs", + "summary": "Lead a department team, own its standards and develop the associates on it.", + "do": [ + "Team Leads at {banner} #{store} in {city}, {state} run a department end to end: staffing the " + "shift, setting priorities at the huddle, working the floor alongside the team and owning the " + "department's sales and in-stock results.", + "You will coach associates day to day, handle customer escalations, and partner with the coach " + "on scheduling and development plans.", + ], + "bring": [ + "Plan and assign daily work for a department team.", + "Coach associates on standards and follow through on development plans.", + "Own department in-stock, shrink and customer experience results.", + "Complies with company policies, procedures, and standards of ethics and integrity.", + ], + "placements": [ + ("144", "Full time", "WD,WE", 19.00, 32.00, 1, None), + ("2073", "Full time", "WD,WE", 20.00, 33.00, 1, None), + ("4137", "Full time", "WD,SD", 21.00, 34.00, 2, None), + ("2610", "Full time", "WE,SE", 18.00, 30.00, 1, None), + ], + }, + { + "title": "Coach", + "area": "stores-and-clubs", + "category": "Retail Management", + "hashtag": "#storeleadershipjobs", + "summary": "Lead several departments and the team leads who run them.", + "do": [ + "Coaches at {banner} #{store} in {city}, {state} lead a group of departments and the team leads " + "inside them, owning results for sales, availability, shrink and associate engagement across " + "that area of the building.", + "You will spend the day on the floor, remove barriers for your leads, and hold the standard " + "when the store is busiest.", + ], + "bring": [ + "Lead team leads and associates across multiple departments.", + "Own area results for sales, in-stock, shrink and engagement.", + "Build talent through structured coaching and succession planning.", + "Complies with company policies, procedures, and standards of ethics and integrity.", + ], + "placements": [ + ("1230", "Full time", "WD,SD", 23.00, 38.00, 1, None), + ("2163", "Full time", "WE,SE", 24.00, 39.00, 1, None), + ], + }, + { + "title": "Fuel Station Associate", + "area": "stores-and-clubs", + "category": "Fuel Station", + "hashtag": "#fuelstationjobs", + "summary": "Run the club fuel station: assist members, check equipment and keep the site compliant.", + "do": [ + "Fuel Station Associates at {banner} #{store} in {city}, {state} greet members at the pumps, " + "help with payment issues, complete daily equipment and environmental checks, and keep the " + "island clean and stocked.", + "You will work outdoors in all weather and follow strict fuel handling and spill response " + "procedures.", + ], + "bring": [ + "Complete daily fuel equipment, tank and environmental compliance checks.", + "Assist members at the pump and resolve payment issues.", + "Follow fuel handling, spill response and emergency shutdown procedures.", + "Complies with company policies, procedures, and standards of ethics and integrity.", + ], + "placements": [ + ("6636", "Part time", "WD,SD,FX", 16.50, 23.50, 2, None), + ("8253", "Part time", "WE,SE", 15.50, 22.50, 1, None), + ("6216", "Part time", "SN,WN", 17.00, 24.00, 2, None), + ("4750", "Full time", "WD,WN", 16.00, 23.00, 1, None), + ], + }, + { + "title": "Auto Care Center Technician", + "area": "stores-and-clubs", + "category": "Auto Care Center", + "hashtag": "#autocarecenterjobs", + "summary": "Perform tire, battery and light maintenance service in the Auto Care Center.", + "do": [ + "Auto Care Center Technicians at {banner} #{store} in {city}, {state} mount and balance tires, " + "install batteries, change oil and complete light maintenance services while the customer " + "shops.", + "You will inspect vehicles honestly, document every service performed, and keep the bay and " + "equipment to safety standard.", + ], + "bring": [ + "Mount, balance and repair tires and install batteries safely.", + "Complete oil changes and light maintenance to manufacturer specification.", + "Document every inspection and service accurately in the shop system.", + "Complies with company policies, procedures, and standards of ethics and integrity.", + ], + "placements": [ + ("954", "Full time", "WD,SD", 17.00, 30.00, 3, None), + ("1230", "Full time", "WD,WE", 17.00, 30.00, 5, None), + ("471", "Part time", "WE,SE", 16.50, 29.00, 2, None), + ("1179", "Full time", "WD,SD", 17.50, 30.50, 1, None), + ("5133", "Full time", "WD,SD", 17.50, 30.50, 2, None), + ], + }, + { + "title": "Tire & Battery Technician", + "area": "stores-and-clubs", + "category": "Auto Services", + "hashtag": "#tireandbatteryjobs", + "summary": "Service member vehicles in the club tire and battery center.", + "do": [ + "Tire & Battery Technicians at {banner} #{store} in {city}, {state} install and rotate tires, " + "test and replace batteries, and complete the free member services the club is known for.", + "You will work the service write-up desk as well as the bay, so clear explanations matter as " + "much as clean work.", + ], + "bring": [ + "Install, rotate and repair tires to torque and safety specification.", + "Test and replace batteries and charging system components.", + "Write up member services clearly and set accurate expectations on timing.", + "Complies with company policies, procedures, and standards of ethics and integrity.", + ], + "placements": [ + ("6608", "Full time", "WD,WE", 19.00, 27.00, 2, None), + ("4750", "Part time", "SD,SE", 17.50, 25.50, 1, None), + ("6216", "Full time", "WD,SD", 18.50, 26.50, 2, None), + ("8259", "Part time", "WE,SN", 17.00, 25.00, 1, None), + ], + }, + { + "title": "Maintenance Technician", + "area": "stores-and-clubs", + "category": "Maintenance", + "hashtag": "#storemaintenancejobs", + "summary": "Keep store equipment, refrigeration and building systems running.", + "do": [ + "Maintenance Technicians at {banner} #{store} in {city}, {state} respond to equipment calls " + "across the building: refrigeration alarms, doors, carts, lighting, HVAC and the compactor.", + "You will complete scheduled preventive maintenance, escalate refrigerant work to the " + "certified contractor, and close every work order with what you actually did.", + ], + "bring": [ + "Diagnose and repair store equipment, lighting and building systems.", + "Complete scheduled preventive maintenance on time.", + "Follow lockout/tagout and electrical safety procedures without exception.", + "Complies with company policies, procedures, and standards of ethics and integrity.", + ], + "placements": [ + ("2073", "Full time", "WN,SN,FX", 20.00, 30.00, 1, None), + ("1399", "Part time", "WE,SE,FX", 18.00, 28.00, 2, None), + ("5439", "Full time", "WD,WE,FX", 19.00, 29.00, 1, None), + ("3512", "Full time", "WD,SD", 17.00, 26.00, 2, None), + ], + }, + { + "title": "Asset Protection Associate", + "area": "stores-and-clubs", + "category": "Security and Asset Protection", + "hashtag": "#assetprotectionjobs", + "summary": "Reduce shrink and keep associates and customers safe inside the store.", + "do": [ + "Asset Protection Associates at {banner} #{store} in {city}, {state} work the floor and the " + "camera room, deter theft, respond to alarms, and partner with store leadership on safety " + "walks and incident follow-up.", + "You will document every incident to policy and work with local law enforcement when the " + "asset protection manager asks you to.", + ], + "bring": [ + "Deter and document theft following company approach and apprehension policy.", + "Monitor camera and EAS systems and respond to alarms.", + "Complete safety walks and incident reports accurately.", + "Complies with company policies, procedures, and standards of ethics and integrity.", + ], + "placements": [ + ("5991", "Full time", "WD,WE,SD", 17.00, 30.00, 2, None), + ("3387", "Part time", "SE,SN", 16.00, 28.00, 1, None), + ("5382", "Full time", "WD,SD", 19.00, 32.00, 1, None), + ("5388", "Full time", "WD,SD", 18.00, 30.00, 2, None), + ], + }, + { + "title": "Asset Protection Customer Specialist", + "area": "stores-and-clubs", + "category": "Security and Asset Protection", + "hashtag": "#apcustomerspecialistjobs", + "summary": "Greet at the entrance, verify receipts and keep the front of the store secure.", + "do": [ + "Asset Protection Customer Specialists at {banner} #{store} in {city}, {state} work the " + "entrance: greeting every customer, verifying receipts at the door, and watching the front end " + "for problems before they grow.", + "You will support the asset protection team with documentation and keep the entry area clean, " + "carted and welcoming.", + ], + "bring": [ + "Greet customers at the entrance and verify receipts at the exit.", + "Watch front-end activity and escalate concerns to asset protection.", + "Keep the entry area stocked with carts and free of hazards.", + "Complies with company policies, procedures, and standards of ethics and integrity.", + ], + "placements": [ + ("2503", "Full time", "WD,WE", 16.00, 26.00, 1, None), + ("5260", "Part time", "WE,SE", 17.00, 28.00, 2, None), + ], + }, + # ------------------------------- Healthcare ---------------------------- + { + "title": "Pharmacy Technician", + "area": "healthcare", + "category": "Pharmacy Services", + "hashtag": "#pharmacytechjobs", + "summary": "Support the pharmacist with intake, data entry, filling and patient pickup.", + "do": [ + "Pharmacy Technicians at {banner} #{store} in {city}, {state} take in prescriptions, enter and " + "verify patient and insurance information, count and label under the pharmacist's supervision, " + "and hand off at the pickup window.", + "You will work third-party rejections, call prescribers for clarifications, and keep the " + "workflow moving so patients are not waiting on paperwork.", + ], + "bring": [ + "Enter prescription and insurance information accurately into the pharmacy system.", + "Fill and label prescriptions under the direct supervision of the pharmacist.", + "Resolve third-party rejections and coordinate with prescriber offices.", + "Complies with company policies, procedures, and standards of ethics and integrity.", + ], + "placements": [ + ("5260", "Full time", "WD,SD", 18.00, 30.00, 2, None), + ("4137", "Part time", "WE,SE", 19.50, 32.00, 1, None), + ("2110", "Full time", "WD,WE", 18.50, 30.50, 1, None), + ("2050", "Part time", "SD,SE", 20.00, 33.00, 2, None), + ("3826", "Full time", "WD,WE", 18.50, 31.00, 3, None), + ], + }, + { + "title": "Certified Pharmacy Technician", + "area": "healthcare", + "category": "Pharmacy Services", + "hashtag": "#certifiedpharmacytechjobs", + "summary": "Work at the top of your certification supporting immunizations and clinical services.", + "do": [ + "Certified Pharmacy Technicians at {banner} #{store} in {city}, {state} do everything a " + "technician does, plus the work that certification unlocks: immunization support, medication " + "therapy outreach and inventory ownership for controlled substances.", + "You will mentor uncertified technicians on workflow and accuracy, and cover the pharmacist's " + "administrative queue during clinical blocks.", + ], + "bring": [ + "Hold and maintain a current state pharmacy technician certification.", + "Support immunization clinics and medication therapy outreach.", + "Own perpetual inventory counts for controlled substances.", + "Complies with company policies, procedures, and standards of ethics and integrity.", + ], + "placements": [ + ("3520", "Full time", "WD,WE", 21.00, 34.00, 1, None), + ("1236", "Part time", "WE,SD", 19.00, 31.00, 2, None), + ], + }, + { + "title": "Optician", + "area": "healthcare", + "category": "Optical Services", + "hashtag": "#opticianjobs", + "summary": "Fit, adjust and dispense eyewear in the Vision Center.", + "do": [ + "Opticians at {banner} #{store} in {city}, {state} interpret prescriptions, take measurements, " + "recommend lens options honestly, and fit and adjust finished eyewear so it is comfortable on " + "day one.", + "You will also run the lab bench work the store handles in house, manage the frame board, and " + "coordinate with the independent optometrist's office next door.", + ], + "bring": [ + "Interpret ophthalmic prescriptions and take accurate fitting measurements.", + "Recommend frames and lens treatments suited to the prescription and budget.", + "Adjust, repair and dispense finished eyewear to specification.", + "Complies with company policies, procedures, and standards of ethics and integrity.", + ], + "placements": [ + ("2073", "Full time", "WD,SD", 21.50, 34.50, 1, None), + ("5382", "Part time", "WE,SE", 23.00, 36.00, 1, None), + ("1236", "Full time", "WD,WE", 21.00, 34.00, 1, None), + ("5991", "Full time", "WD,WE,SD", 22.00, 35.00, 2, None), + ("5133", "Full time", "WD,SD", 21.50, 34.50, 1, None), + ("3826", "Part time", "WE,SE", 22.50, 35.50, 1, None), + ], + }, + { + "title": "Vision Center Associate", + "area": "healthcare", + "category": "Optical Services", + "hashtag": "#visioncenterjobs", + "summary": "Greet vision center customers, schedule exams and support the optician.", + "do": [ + "Vision Center Associates at {banner} #{store} in {city}, {state} welcome customers, schedule " + "exams with the on-site optometrist, verify vision benefits and support the optician with " + "dispensing and repairs.", + "You will keep the frame board merchandised and the exam schedule full without overbooking.", + ], + "bring": [ + "Schedule exams and verify vision insurance benefits.", + "Support dispensing, adjustments and simple frame repairs.", + "Keep the frame board merchandised, priced and clean.", + "Complies with company policies, procedures, and standards of ethics and integrity.", + ], + "placements": [ + ("471", "Part time", "WD,SD", 16.00, 26.00, 2, None), + ("5439", "Part time", "WE,SE", 17.00, 27.00, 1, None), + ], + }, + { + "title": "Health & Wellness Operations Associate", + "area": "healthcare", + "category": "Health and Wellness Operations", + "hashtag": "#healthandwellnessjobs", + "summary": "Keep the health and wellness area stocked, compliant and ready for patients.", + "do": [ + "Health & Wellness Operations Associates at {banner} #{store} in {city}, {state} own the " + "operational side of the department: over-the-counter stocking, expiration audits, compliance " + "logs and the patient waiting area.", + "You will support screening events, keep the private consultation room ready, and route " + "patient questions to the pharmacist correctly.", + ], + "bring": [ + "Complete over-the-counter stocking and expiration audits on schedule.", + "Maintain compliance logs and the private consultation area.", + "Support screening and immunization events with setup and intake.", + "Complies with company policies, procedures, and standards of ethics and integrity.", + ], + "placements": [ + ("2050", "Full time", "WD,WE", 18.00, 29.00, 1, None), + ("3387", "Part time", "SD,SE", 17.00, 28.00, 2, None), + ("3512", "Part time", "WD,SD", 17.50, 28.50, 1, None), + ("1399", "Part time", "WE,SN", 18.50, 29.50, 2, None), + ], + }, + { + "title": "Certified Medical Assistant", + "area": "healthcare", + "category": "Clinical Care", + "hashtag": "#clinicalcarejobs", + "summary": "Room patients, take vitals and support the clinician in a community care setting.", + "do": [ + "Certified Medical Assistants supporting {banner} #{store} in {city}, {state} greet and room " + "patients, take vitals and history, prepare the room, and assist the clinician during the " + "visit.", + "You will document in the electronic health record, handle specimen collection and labelling, " + "and close out visit instructions with the patient before they leave.", + ], + "bring": [ + "Hold a current medical assistant certification and BLS card.", + "Take and document vitals, history and medication reconciliation accurately.", + "Collect and label specimens following chain-of-custody procedures.", + "Complies with company policies, procedures, and standards of ethics and integrity.", + ], + "placements": [ + ("1236", "Full time", "WD,WE", 19.00, 30.00, 1, None), + ("3520", "Part time", "WD,SD", 22.00, 34.00, 2, None), + ("2610", "Part time", "WE,SE", 19.50, 30.50, 1, None), + ("954", "Part time", "WD,SD", 18.00, 29.00, 2, None), + ], + }, + # ------------------------------- Students (hourly) --------------------- + { + "title": "Retail Operations Intern", + "area": "students", + "category": "Internship", + "hashtag": "#walmartinternships", + "summary": "A paid store internship rotating through front end, digital and merchandising.", + "do": [ + "Retail Operations Interns at {banner} #{store} in {city}, {state} spend the term rotating " + "through the front end, the digital pickup team and a merchandising department, with a store " + "leader as your mentor.", + "You will finish the program by presenting one operational improvement you scoped, tested and " + "measured inside the building.", + ], + "bring": [ + "Currently enrolled in an associate or bachelor's degree program.", + "Availability for a full internship term including some weekend coverage.", + "Willingness to work the floor in every rotation, not just observe.", + "Complies with company policies, procedures, and standards of ethics and integrity.", + ], + "placements": [ + ("144", "Intern", "WD,FX", 16.00, 20.00, 1, None), + ("2073", "Intern", "WD,FX", 16.50, 20.50, 1, None), + ("4137", "Intern", "WE,FX", 17.00, 21.00, 1, None), + ("2503", "Intern", "WD,SD", 15.00, 19.00, 1, None), + ("2050", "Intern", "WD,FX", 16.25, 20.25, 1, None), + ("1179", "Intern", "WE,FX", 16.75, 20.75, 1, None), + ("3387", "Intern", "WD,SD", 15.50, 19.50, 1, None), + ], + }, + { + "title": "Club Operations Intern", + "area": "students", + "category": "Internship", + "hashtag": "#samsclubinternships", + "summary": "A paid club internship focused on membership growth and fresh operations.", + "do": [ + "Club Operations Interns at {banner} #{store} in {city}, {state} work with the club manager on " + "membership growth, fresh area operations and the weekly merchandising plan.", + "You will own one measurable project for the term and present the results to the club " + "leadership team.", + ], + "bring": [ + "Currently enrolled in an associate or bachelor's degree program.", + "Interest in retail operations, membership models or fresh category management.", + "Availability for a full internship term including some weekend coverage.", + "Complies with company policies, procedures, and standards of ethics and integrity.", + ], + "placements": [ + ("6608", "Intern", "WD,FX", 18.00, 22.00, 1, None), + ("6318", "Intern", "WE,FX", 16.50, 20.50, 1, None), + ("8259", "Intern", "WD,SD", 17.25, 21.25, 1, None), + ("6216", "Intern", "WE,FX", 17.75, 21.75, 1, None), + ("4744", "Intern", "WD,FX", 17.00, 21.00, 1, None), + ("8155", "Intern", "WE,FX", 17.50, 21.50, 1, None), + ], + }, +] + +# --------------------------------------------------------------------------- # +# Salaried title families. +# +# placement tuple: (store_number, employment_type, min_pay, max_pay, +# worker_type, qual_slots, extras) +# `qual_slots` = (degree_field, option1_years, option2_years, preferred_slot) +# `extras` is an optional dict: {"job_id": ..., "qual_clause": ...} +# Minimum-qualification text is built from the family's template with those +# slots, so every posting's Option 1 / Option 2 text is unique. A `qual_clause` +# is appended to both options ("..., including .") so that posting's +# qualification text is specific to this mirror. +# +# Pinned job_ids are synthetic: none of them is a requisition ID that exists on +# careers.walmart.com. +# --------------------------------------------------------------------------- # +SALARIED_FAMILIES = [ + { + "title": "Staff, Software Engineer - Backend / ML", + "area": "technology", + "category": "Software Engineering and Architecture", + "shifts": "WD,SD", + "summary": "Set the technical direction for backend microservices and ML-serving " + "infrastructure at retail scale.", + "do": [ + "As a Staff Software Engineer at {location_name} in {city}, {state}, you'll be a technical " + "leader who defines the direction for and evolves the backend microservices, data pipelines, " + "and ML-serving infrastructure that power search at massive scale. You'll lead a team of six " + "to ten engineers, set the technical vision for critical systems, and drive the quality bar " + "across the team.", + "We're in an active phase of platform modernization - redesigning and refactoring core " + "systems. If you want to build, not just maintain, this is the right time to join.", + ], + "about_team": "The eCommerce Search engineering team owns the end-to-end technology stack that " + "powers product search and discovery across Walmart's global eCommerce channels, " + "backed by microservices, large-scale data and feature pipelines, search engines, " + "and ML model serving infrastructure.", + "min_qual_option1": "Option 1: Bachelor's degree in {degree_field} and {y1} years' experience in " + "software engineering or related area.", + "min_qual_option2": "Option 2: {y2} years' experience in software engineering or related area.", + "preferred": "Master's degree in {degree_field} and {yp} years' experience in software " + "engineering or related area. We value candidates with a background in creating " + "inclusive digital experiences and knowledge of Web Content Accessibility " + "Guidelines (WCAG) 2.2 AA standards.", + "placements": [ + ("11807", "Full time", 143000, 286000, "Regular/Permanent", + ("computer science, computer engineering, computer information systems, software engineering, or related area", 5, 7, 2), + {"job_id": "R-2468347", + "qual_clause": "including experience operating search or ML-serving systems in production"}), + ("10101", "Full time", 132000, 264000, "Regular/Permanent", + ("computer science, computer engineering, or related area", 5, 8, 3), None), + ("12200", "Full time", 128000, 246000, "Regular/Permanent", + ("computer science, electrical engineering, or related area", 6, 9, 3), None), + ], + }, + { + "title": "Senior Software Engineer", + "area": "technology", + "category": "Software Engineering and Architecture", + "shifts": "WD", + "summary": "Design, build and operate the services behind checkout, fulfillment and search.", + "do": [ + "Senior Software Engineers at {location_name} in {city}, {state} own services end to end: " + "design, implementation, deployment and on-call. You will partner with product and data " + "science to turn ambiguous problems into systems that hold up at Walmart's traffic.", + "You will review designs and code across the team, mentor engineers earlier in their careers, " + "and keep an eye on cost, latency and reliability as much as on features.", + ], + "about_team": "This team builds and runs the platform services that thousands of engineers and " + "millions of customers depend on every day.", + "min_qual_option1": "Option 1: Bachelor's degree in {degree_field} and {y1} years' experience in " + "software engineering or related area.", + "min_qual_option2": "Option 2: {y2} years' experience in software engineering or related area.", + "preferred": "Master's degree in {degree_field} and {yp} years' experience building distributed " + "systems in production.", + "placements": [ + ("11003", "Full time", 110000, 220000, "Regular/Permanent", + ("computer science, computer information systems, or related area", 4, 7, 1), + {"qual_clause": "including experience building high-volume checkout or payments services"}), + ("11807", "Full time", 117000, 234000, "Regular/Permanent", + ("computer engineering, software engineering, or related area", 4, 7, 2), None), + ("10101", "Full time", 96000, 192000, "Regular/Permanent", + ("information systems, computer science, or related area", 2, 4, 1), None), + ("12200", "Full time", 105000, 195000, "Regular/Permanent", + ("embedded systems, computer engineering, or related area", 3, 6, 2), None), + ], + }, + { + "title": "Software Engineer III", + "area": "technology", + "category": "Software Engineering and Architecture", + "shifts": "WD", + "summary": "Build features across the smart TV platform and its content services.", + "do": [ + "Software Engineers at {location_name} in {city}, {state} build and ship features across the " + "platform, from the on-device experience to the services behind it.", + "You will write production code every week, take part in design reviews, and work with QA and " + "product on release readiness.", + ], + "about_team": "The platform engineering group builds the software that runs on millions of " + "connected devices in customers' living rooms.", + "min_qual_option1": "Option 1: Bachelor's degree in {degree_field} and {y1} years' experience in " + "software engineering or related area.", + "min_qual_option2": "Option 2: {y2} years' experience in software engineering or related area.", + "preferred": "Experience with embedded platforms and {yp} years' experience shipping consumer " + "software at scale.", + "placements": [ + ("12200", "Full time", 90000, 180000, "Regular/Permanent", + ("computer science or related area", 2, 4, 3), {"job_id": "R-2417063"}), + ], + }, + { + "title": "Senior Manager, Product Management", + "area": "technology", + "category": "Product Management", + "shifts": "WD", + "summary": "Own a product area end to end, from strategy through launch and iteration.", + "do": [ + "Senior Managers of Product Management at {location_name} in {city}, {state} own a product " + "area: the strategy, the roadmap, the trade-offs and the results.", + "You will work daily with engineering, design and data science, and you will be the person " + "who says no often enough that the yes means something.", + ], + "about_team": "Product management at Walmart sits close to the customer and close to the code.", + "min_qual_option1": "Option 1: Bachelor's degree in {degree_field} and {y1} years' experience in " + "product management or related area.", + "min_qual_option2": "Option 2: {y2} years' experience in product management or related area.", + "preferred": "Master's degree in business administration and {yp} years' experience leading " + "product teams.", + "placements": [ + ("10101", "Full time", 110000, 220000, "Regular/Permanent", + ("business, analytics, engineering, or related area", 5, 7, 2), {"job_id": "R-2418512"}), + ("11807", "Full time", 132000, 264000, "Regular/Permanent", + ("computer science, business, or related area", 6, 9, 3), None), + ("11003", "Full time", 90000, 180000, "Regular/Permanent", + ("marketing, business, or related area", 4, 6, 1), None), + ("12200", "Full time", 118000, 225000, "Regular/Permanent", + ("electrical engineering, product design, or related area", 5, 8, 3), None), + ], + }, + { + "title": "Director, Product Management", + "area": "technology", + "category": "Product Management", + "shifts": "WD,SD", + "summary": "Lead a portfolio of product areas and the managers who run them.", + "do": [ + "Directors of Product Management at {location_name} in {city}, {state} set direction for a " + "portfolio, hire and develop product managers, and represent the portfolio in company-level " + "planning.", + "You will spend your time on strategy, talent and unblocking - not on writing every " + "requirement yourself.", + ], + "about_team": "This portfolio spans several teams working on connected customer experiences.", + "min_qual_option1": "Option 1: Bachelor's degree in {degree_field} and {y1} years' experience in " + "product management or related area, including {yp} years of people leadership.", + "min_qual_option2": "Option 2: {y2} years' experience in product management or related area, " + "including {yp} years of people leadership.", + "preferred": "Master's degree in business administration and experience owning a profit and loss " + "statement for {yp} years or more.", + "placements": [ + ("10101", "Full time", 130000, 260000, "Regular/Permanent", + ("business, engineering, or related area", 8, 11, 4), None), + ("12200", "Full time", 125000, 245000, "Regular/Permanent", + ("electrical engineering, business, or related area", 7, 10, 4), None), + ], + }, + { + "title": "Senior Data Scientist", + "area": "technology", + "category": "Data Science and Analytics", + "shifts": "WD", + "summary": "Build models that change what customers see and what the business decides.", + "do": [ + "Senior Data Scientists at {location_name} in {city}, {state} frame the problem, build the " + "model, ship it behind an experiment, and tell the story of what it did.", + "You will work in Python and SQL against very large datasets, and you will be expected to " + "defend your methodology to people who will use the results.", + ], + "about_team": "Data science sits inside the product teams here, not in a separate lab.", + "min_qual_option1": "Option 1: Bachelor's degree in {degree_field} and {y1} years' experience in " + "an analytics or data science role.", + "min_qual_option2": "Option 2: {y2} years' experience in an analytics or data science role.", + "preferred": "Master's or PhD in {degree_field} and {yp} years' experience deploying models to " + "production.", + "placements": [ + ("10101", "Full time", 108000, 216000, "Regular/Permanent", + ("statistics, economics, computer science, or related area", 4, 6, 2), None), + ("11807", "Full time", 130000, 260000, "Regular/Permanent", + ("machine learning, statistics, or related area", 5, 8, 3), None), + ("11003", "Full time", 110000, 190000, "Regular/Permanent", + ("applied mathematics, statistics, or related area", 3, 5, 1), None), + ("11500", "Full time", 100000, 175000, "Regular/Permanent", + ("operations research, statistics, or related area", 3, 6, 2), None), + ("12200", "Full time", 112000, 205000, "Regular/Permanent", + ("data science, statistics, or related area", 4, 7, 2), None), + ], + }, + { + "title": "Senior Manager, Information Security", + "area": "technology", + "category": "Information Security", + "shifts": "WD,SD", + "summary": "Lead a security function protecting customer and associate data at scale.", + "do": [ + "Senior Managers of Information Security at {location_name} in {city}, {state} lead a security " + "team, set the control standard for their domain, and partner with engineering on how those " + "controls actually get implemented.", + "You will own incident response readiness for your area and report risk posture to leadership " + "on a regular cadence.", + ], + "about_team": "Information security here is embedded with the teams it protects.", + "min_qual_option1": "Option 1: Bachelor's degree in {degree_field} and {y1} years' experience in " + "information security or related area.", + "min_qual_option2": "Option 2: {y2} years' experience in information security or related area.", + "preferred": "CISSP or equivalent certification and {yp} years' experience leading security teams.", + "placements": [ + ("10101", "Full time", 115000, 230000, "Regular/Permanent", + ("information technology, cybersecurity, or related area", 5, 8, 3), None), + ("11807", "Full time", 140000, 280000, "Regular/Permanent", + ("computer science, cybersecurity, or related area", 6, 9, 4), None), + ("12200", "Full time", 122000, 235000, "Regular/Permanent", + ("information assurance, computer science, or related area", 4, 7, 3), None), + ], + }, + { + "title": "Information Security Engineer III", + "area": "technology", + "category": "Information Security", + "shifts": "WD", + "summary": "Engineer and operate the detection and prevention controls that protect the platform.", + "do": [ + "Security Engineers at {location_name} in {city}, {state} build detections, tune controls, and " + "work incidents alongside the response team.", + "You will write code, not just configure tools, and you will be on a rotation.", + ], + "about_team": "This team keeps the platform defensible as it changes weekly.", + "min_qual_option1": "Option 1: Bachelor's degree in {degree_field} and {y1} years' experience in " + "information security or related area.", + "min_qual_option2": "Option 2: {y2} years' experience in information security or related area.", + "preferred": "Experience with cloud security tooling and {yp} years' experience in detection " + "engineering.", + "placements": [ + ("11003", "Full time", 80000, 160000, "Regular/Permanent", + ("cybersecurity, information systems, or related area", 2, 4, 1), None), + ("12200", "Full time", 95000, 170000, "Regular/Permanent", + ("computer engineering, cybersecurity, or related area", 3, 5, 2), None), + ], + }, + { + "title": "Senior UX Designer", + "area": "technology", + "category": "Creative Design and UX", + "shifts": "WD", + "summary": "Design flows that millions of people use without thinking about them.", + "do": [ + "Senior UX Designers at {location_name} in {city}, {state} own the experience for a product " + "area: research synthesis, flows, prototypes and the detailed specs engineering builds from.", + "You will test your work with real customers and change it when the test says so.", + ], + "about_team": "Design partners directly with product and engineering from the first week of a " + "project.", + "min_qual_option1": "Option 1: Bachelor's degree in {degree_field} and {y1} years' experience in " + "user experience design or related area.", + "min_qual_option2": "Option 2: {y2} years' experience in user experience design or related area.", + "preferred": "A portfolio showing shipped consumer work and {yp} years' experience with design " + "systems.", + "placements": [ + ("11807", "Full time", 120000, 240000, "Regular/Permanent", + ("design, human-computer interaction, or related area", 5, 7, 3), None), + ("11003", "Full time", 96000, 186000, "Regular/Permanent", + ("interaction design, visual design, or related area", 3, 6, 2), None), + ("12200", "Full time", 88000, 165000, "Regular/Permanent", + ("industrial design, human factors, or related area", 3, 5, 2), None), + ], + }, + { + "title": "Principal UX Researcher", + "area": "technology", + "category": "Creative Design and UX", + "shifts": "WD", + "summary": "Set the research agenda for a large product organization.", + "do": [ + "Principal UX Researchers at {location_name} in {city}, {state} decide what the organization " + "needs to learn next, design the studies that answer it, and make sure the answer changes " + "what gets built.", + "You will mentor researchers across teams and raise the methodological bar for everyone.", + ], + "about_team": "Research here reports into design and works across several product areas at once.", + "min_qual_option1": "Option 1: Master's degree in {degree_field} and {y1} years' experience in " + "user research or related area.", + "min_qual_option2": "Option 2: {y2} years' experience in user research or related area.", + "preferred": "PhD in {degree_field} and {yp} years' experience leading mixed-methods research " + "programs.", + "placements": [ + ("11807", "Full time", 150000, 275000, "Regular/Permanent", + ("psychology, human-computer interaction, or related area", 7, 10, 4), None), + ("12200", "Full time", 138000, 255000, "Regular/Permanent", + ("cognitive science, design research, or related area", 6, 9, 3), None), + ], + }, + { + "title": "Senior Technical Program Manager", + "area": "technology", + "category": "Technical Program Management", + "shifts": "WD", + "summary": "Drive cross-team technical programs from commitment to launch.", + "do": [ + "Senior Technical Program Managers at {location_name} in {city}, {state} own the plan, the " + "risks and the communication for programs that span several engineering teams.", + "You will be technical enough to challenge an estimate and organized enough that nobody has " + "to ask you for a status.", + ], + "about_team": "Technical program management sits with engineering leadership here.", + "min_qual_option1": "Option 1: Bachelor's degree in {degree_field} and {y1} years' experience in " + "technical program management or related area.", + "min_qual_option2": "Option 2: {y2} years' experience in technical program management or related " + "area.", + "preferred": "Experience running programs across distributed teams for {yp} years or more.", + "placements": [ + ("11807", "Full time", 125000, 250000, "Regular/Permanent", + ("engineering, computer science, or related area", 5, 8, 3), None), + ("10101", "Full time", 105000, 210000, "Regular/Permanent", + ("information systems, engineering, or related area", 4, 7, 2), None), + ("12200", "Full time", 92000, 175000, "Regular/Permanent", + ("electrical engineering, computer science, or related area", 3, 6, 2), None), + ("11003", "Full time", 99000, 195000, "Regular/Permanent", + ("industrial engineering, business, or related area", 4, 6, 2), None), + ], + }, + { + "title": "IT Support Engineer", + "area": "technology", + "category": "Information Technology", + "shifts": "WD,SD", + "summary": "Keep the people who work here productive, from laptops to conference rooms.", + "do": [ + "IT Support Engineers at {location_name} in {city}, {state} handle escalated endpoint, " + "identity and collaboration issues for the associates on site.", + "You will automate the repeat offenders instead of fixing them one ticket at a time.", + ], + "about_team": "Workplace technology supports every associate in the building.", + "min_qual_option1": "Option 1: Associate's degree in {degree_field} and {y1} years' experience in " + "information technology support or related area.", + "min_qual_option2": "Option 2: {y2} years' experience in information technology support or " + "related area.", + "preferred": "Scripting experience and {yp} years' experience with endpoint management tooling.", + "placements": [ + ("10101", "Full time", 70000, 140000, "Regular/Permanent", + ("information technology or related area", 2, 4, 2), None), + ("11500", "Full time", 68000, 136000, "Regular/Permanent", + ("computer information systems or related area", 2, 5, 1), None), + ("12200", "Full time", 72000, 144000, "Regular/Permanent", + ("network administration or related area", 3, 5, 2), None), + ("11109", "Full time", 66000, 132000, "Regular/Permanent", + ("information systems or related area", 1, 3, 1), None), + ], + }, + { + "title": "Senior Manager, Finance", + "area": "corporate", + "category": "Accounting and Finance", + "shifts": "WD", + "summary": "Lead financial planning and analysis for a business unit.", + "do": [ + "Senior Managers of Finance at {location_name} in {city}, {state} run the planning cycle for " + "their business unit, build the models leadership decides from, and lead a small team of " + "analysts.", + "You will be in the room when the trade-offs are made, and you will be expected to have a " + "point of view.", + ], + "about_team": "Finance partners are embedded with the businesses they support.", + "min_qual_option1": "Option 1: Bachelor's degree in {degree_field} and {y1} years' experience in " + "accounting, finance or related area.", + "min_qual_option2": "Option 2: {y2} years' experience in accounting, finance or related area.", + "preferred": "CPA or MBA and {yp} years' experience leading finance teams.", + "placements": [ + ("10101", "Full time", 100000, 200000, "Regular/Permanent", + ("accounting, finance, or related area", 5, 7, 3), None), + ("11500", "Full time", 90000, 180000, "Regular/Permanent", + ("finance, economics, or related area", 4, 6, 2), None), + ("12200", "Full time", 98000, 190000, "Regular/Permanent", + ("corporate finance, accounting, or related area", 3, 5, 2), None), + ], + }, + { + "title": "Financial Analyst III", + "area": "corporate", + "category": "Accounting and Finance", + "shifts": "WD", + "summary": "Build the forecasts, variance analysis and business cases the team runs on.", + "do": [ + "Financial Analysts at {location_name} in {city}, {state} own a piece of the forecast, explain " + "variances to plan, and build the business cases that support investment decisions.", + "You will live in spreadsheets and the planning system, and you will present your work " + "directly to business leaders.", + ], + "about_team": "This team supports one of the largest cost centers in the company.", + "min_qual_option1": "Option 1: Bachelor's degree in {degree_field} and {y1} years' experience in " + "financial analysis or related area.", + "min_qual_option2": "Option 2: {y2} years' experience in financial analysis or related area.", + "preferred": "Advanced modelling skills and {yp} years' experience in a retail or supply chain " + "finance team.", + "placements": [ + ("10101", "Full time", 70000, 130000, "Regular/Permanent", + ("finance, accounting, or related area", 2, 4, 2), None), + ("11109", "Full time", 68000, 126000, "Regular/Permanent", + ("accounting, business, or related area", 2, 5, 1), None), + ("11500", "Full time", 72000, 134000, "Regular/Permanent", + ("economics, finance, or related area", 3, 5, 2), None), + ("12200", "Full time", 71000, 132000, "Regular/Permanent", + ("finance, business analytics, or related area", 1, 3, 1), None), + ], + }, + { + "title": "Senior Manager, People Partner", + "area": "corporate", + "category": "Human Resources", + "shifts": "WD", + "summary": "Partner with business leaders on talent, org design and associate experience.", + "do": [ + "Senior Managers, People Partner at {location_name} in {city}, {state} advise leaders on " + "organisation design, talent planning and the hard conversations, and own the people plan for " + "their client group.", + "You will use data as well as judgement, and you will be the person associates trust to be " + "straight with them.", + ], + "about_team": "People partners support the business teams in the building directly.", + "min_qual_option1": "Option 1: Bachelor's degree in {degree_field} and {y1} years' experience in " + "human resources or related area.", + "min_qual_option2": "Option 2: {y2} years' experience in human resources or related area.", + "preferred": "SHRM-SCP certification and {yp} years' experience supporting technology " + "organisations.", + "placements": [ + ("10101", "Full time", 96000, 186000, "Regular/Permanent", + ("human resources, business, or related area", 5, 7, 3), None), + ("11003", "Full time", 100000, 195000, "Regular/Permanent", + ("industrial relations, psychology, or related area", 4, 6, 2), None), + ], + }, + { + "title": "HR Business Partner", + "area": "corporate", + "category": "Human Resources", + "shifts": "WD", + "summary": "Support a client group across hiring, performance and associate relations.", + "do": [ + "HR Business Partners at {location_name} in {city}, {state} run the people cycle for their " + "client group: hiring plans, performance calibration, development and associate relations " + "cases.", + "You will coach managers who are new to leading people and hold the line on policy when it " + "matters.", + ], + "about_team": "This team supports several hundred associates across the site.", + "min_qual_option1": "Option 1: Bachelor's degree in {degree_field} and {y1} years' experience in " + "human resources or related area.", + "min_qual_option2": "Option 2: {y2} years' experience in human resources or related area.", + "preferred": "Experience with associate relations investigations for {yp} years or more.", + "placements": [ + ("11500", "Full time", 80000, 150000, "Regular/Permanent", + ("human resources or related area", 3, 5, 2), None), + ("11109", "Full time", 78000, 146000, "Regular/Permanent", + ("business administration or related area", 2, 4, 1), None), + ("12200", "Full time", 82000, 152000, "Regular/Permanent", + ("organizational psychology, human resources, or related area", 4, 6, 2), None), + ], + }, + { + "title": "Manager, Marketing", + "area": "corporate", + "category": "Marketing and Advertising", + "shifts": "WD", + "summary": "Own campaign strategy and execution for a product line.", + "do": [ + "Marketing Managers at {location_name} in {city}, {state} own the plan for a product line: " + "positioning, campaign calendar, agency briefs and the results readout.", + "You will work with creative, media and analytics, and you will be accountable for what the " + "spend returned.", + ], + "about_team": "Marketing here works close to the product teams and the sales calendar.", + "min_qual_option1": "Option 1: Bachelor's degree in {degree_field} and {y1} years' experience in " + "marketing or related area.", + "min_qual_option2": "Option 2: {y2} years' experience in marketing or related area.", + "preferred": "Experience running integrated consumer campaigns for {yp} years or more.", + "placements": [ + ("12200", "Full time", 85000, 160000, "Regular/Permanent", + ("marketing, communications, or related area", 3, 5, 2), None), + ("10101", "Full time", 90000, 170000, "Regular/Permanent", + ("marketing, business, or related area", 4, 6, 3), None), + ], + }, + { + "title": "Senior Manager, Brand Marketing", + "area": "corporate", + "category": "Marketing and Advertising", + "shifts": "WD", + "summary": "Lead brand strategy and the campaigns that carry it.", + "do": [ + "Senior Managers of Brand Marketing at {location_name} in {city}, {state} own how the brand " + "shows up: the platform, the creative standard and the campaigns that put it in front of " + "customers.", + "You will lead a small team and manage agency partners against a real budget.", + ], + "about_team": "Brand marketing sets the standard the rest of marketing works to.", + "min_qual_option1": "Option 1: Bachelor's degree in {degree_field} and {y1} years' experience in " + "brand or consumer marketing.", + "min_qual_option2": "Option 2: {y2} years' experience in brand or consumer marketing.", + "preferred": "Master's degree in business administration and {yp} years' experience managing " + "agency relationships.", + "placements": [ + ("10101", "Full time", 110000, 210000, "Regular/Permanent", + ("marketing, advertising, or related area", 6, 8, 3), None), + ("11500", "Full time", 105000, 200000, "Regular/Permanent", + ("communications, marketing, or related area", 5, 7, 2), None), + ("12200", "Full time", 102000, 196000, "Regular/Permanent", + ("brand management, marketing, or related area", 4, 6, 2), None), + ], + }, + { + "title": "Marketing Specialist III", + "area": "corporate", + "category": "Marketing and Advertising", + "shifts": "WD", + "summary": "Execute member marketing programs and report on what they returned.", + "do": [ + "Marketing Specialists at {location_name} in {city}, {state} execute the member marketing " + "calendar: briefs, asset trafficking, channel setup and post-campaign reporting.", + "You will keep several campaigns moving at once and be the person who notices the detail " + "everyone else missed.", + ], + "about_team": "Member marketing owns how the club talks to its members between visits.", + "min_qual_option1": "Option 1: Bachelor's degree in {degree_field} and {y1} years' experience in " + "marketing or related area.", + "min_qual_option2": "Option 2: {y2} years' experience in marketing or related area.", + "preferred": "Experience with customer relationship management platforms for {yp} years or more.", + "placements": [ + ("11109", "Full time", 65000, 120000, "Regular/Permanent", + ("marketing or related area", 2, 4, 1), None), + ("12200", "Full time", 68000, 126000, "Regular/Permanent", + ("marketing, media studies, or related area", 3, 5, 2), None), + ], + }, + { + "title": "Senior Buyer", + "area": "corporate", + "category": "Merchandising", + "shifts": "WD", + "summary": "Own assortment, cost and supplier relationships for a category.", + "do": [ + "Senior Buyers at {location_name} in {city}, {state} own a category: what we carry, what we " + "pay for it, and how it performs on the floor.", + "You will negotiate with suppliers, build the assortment plan by season, and answer for the " + "category's sales and margin every month.", + ], + "about_team": "Merchandising decides what ends up on the shelf and at what price.", + "min_qual_option1": "Option 1: Bachelor's degree in {degree_field} and {y1} years' experience in " + "merchandising, buying or related area.", + "min_qual_option2": "Option 2: {y2} years' experience in merchandising, buying or related area.", + "preferred": "Experience negotiating national supplier agreements for {yp} years or more.", + "placements": [ + ("10101", "Full time", 95000, 185000, "Regular/Permanent", + ("business, merchandising, or related area", 5, 7, 3), None), + ("11109", "Full time", 92000, 178000, "Regular/Permanent", + ("supply chain, business, or related area", 4, 6, 2), None), + ], + }, + { + "title": "Merchandising Manager", + "area": "corporate", + "category": "Merchandising", + "shifts": "WD", + "summary": "Turn category strategy into the plan the stores and clubs actually execute.", + "do": [ + "Merchandising Managers at {location_name} in {city}, {state} translate category strategy into " + "modulars, promotions and in-club execution plans.", + "You will work with buyers, replenishment and field leadership to make sure the plan survives " + "contact with the sales floor.", + ], + "about_team": "This team bridges the buying office and the buildings.", + "min_qual_option1": "Option 1: Bachelor's degree in {degree_field} and {y1} years' experience in " + "merchandising or related area.", + "min_qual_option2": "Option 2: {y2} years' experience in merchandising or related area.", + "preferred": "Field retail experience and {yp} years' experience with space planning tools.", + "placements": [ + ("10101", "Full time", 88000, 170000, "Regular/Permanent", + ("merchandising, business, or related area", 4, 6, 2), None), + ("11109", "Full time", 85000, 165000, "Regular/Permanent", + ("retail management, business, or related area", 3, 5, 2), None), + ], + }, + { + "title": "Replenishment Manager", + "area": "corporate", + "category": "Merchandising", + "shifts": "WD", + "summary": "Own in-stock and inventory turns for a category across the network.", + "do": [ + "Replenishment Managers at {location_name} in {city}, {state} own in-stock, forecast accuracy " + "and inventory turns for their categories across the whole network.", + "You will tune forecasting parameters, work supplier lead times, and be the first call when a " + "category goes out of stock in a region.", + ], + "about_team": "Replenishment keeps thousands of buildings full without drowning them in " + "inventory.", + "min_qual_option1": "Option 1: Bachelor's degree in {degree_field} and {y1} years' experience in " + "replenishment, supply chain or related area.", + "min_qual_option2": "Option 2: {y2} years' experience in replenishment, supply chain or related " + "area.", + "preferred": "Experience with demand forecasting systems for {yp} years or more.", + "placements": [ + ("10101", "Full time", 84000, 162000, "Regular/Permanent", + ("supply chain, industrial engineering, or related area", 4, 6, 2), None), + ], + }, + { + "title": "Senior Manager, Delivery Search, Arrival & Matching (Last Mile Delivery)", + "area": "corporate", + "category": "Business Operations", + "shifts": "WD,SD", + "summary": "Own the operations behind driver matching and arrival accuracy for last mile delivery.", + "do": [ + "Senior Managers on Last Mile Delivery at {location_name} in {city}, {state} own the " + "operational levers behind delivery search, driver arrival and order matching: the policies, " + "the thresholds and the escalation paths that keep deliveries on time.", + "You will work with product and data science on where the model ends and operations begins, " + "and you will own the metric either way.", + ], + "about_team": "Last Mile Delivery moves millions of orders from the building to the doorstep.", + "min_qual_option1": "Option 1: Bachelor's degree in {degree_field} and {y1} years' experience in " + "operations management or related area.", + "min_qual_option2": "Option 2: {y2} years' experience in operations management or related area.", + "preferred": "Master's degree in {degree_field} and {yp} years' experience in last mile or " + "transportation operations.", + "placements": [ + ("10101", "Full time", 110000, 220000, "Regular/Permanent", + ("supply chain management, operations, or related area", 6, 9, 3), + {"job_id": "R-2456729", + "qual_clause": "including experience running a last mile delivery or courier network"}), + ("11003", "Full time", 117000, 234000, "Regular/Permanent", + ("industrial engineering, logistics, or related area", 4, 6, 2), + {"qual_clause": "including experience with driver dispatch or arrival-time modeling"}), + ], + }, + { + "title": "Manager, Supply Chain Operations", + "area": "corporate", + "category": "Business Operations", + "shifts": "WD", + "summary": "Run network planning and continuous improvement for a supply chain region.", + "do": [ + "Managers of Supply Chain Operations at {location_name} in {city}, {state} own network " + "planning, cost-to-serve analysis and continuous improvement projects for their region.", + "You will spend time in the buildings, not only in the model, and you will bring changes back " + "that the operators can actually run.", + ], + "about_team": "Supply chain operations connects the network plan to what happens on the dock.", + "min_qual_option1": "Option 1: Bachelor's degree in {degree_field} and {y1} years' experience in " + "supply chain or operations.", + "min_qual_option2": "Option 2: {y2} years' experience in supply chain or operations.", + "preferred": "Lean or Six Sigma certification and {yp} years' experience leading improvement " + "projects.", + "placements": [ + ("11500", "Full time", 82000, 158000, "Regular/Permanent", + ("supply chain management or related area", 3, 5, 2), None), + ("10101", "Full time", 86000, 166000, "Regular/Permanent", + ("industrial engineering, logistics, or related area", 4, 6, 2), None), + ("12200", "Full time", 88000, 168000, "Regular/Permanent", + ("operations management, logistics, or related area", 2, 4, 1), None), + ], + }, + { + "title": "Business Operations Analyst III", + "area": "corporate", + "category": "Business Operations", + "shifts": "WD", + "summary": "Turn operational data into the decisions the business runs on.", + "do": [ + "Business Operations Analysts at {location_name} in {city}, {state} build the reporting, run " + "the analysis and write the recommendation that leadership acts on.", + "You will be trusted with the numbers, which means you will be the one who has to catch the " + "mistake in them.", + ], + "about_team": "Business operations supports planning and performance management across the site.", + "min_qual_option1": "Option 1: Bachelor's degree in {degree_field} and {y1} years' experience in " + "business analysis or related area.", + "min_qual_option2": "Option 2: {y2} years' experience in business analysis or related area.", + "preferred": "Advanced SQL and visualization experience for {yp} years or more.", + "placements": [ + ("11500", "Full time", 66000, 122000, "Regular/Permanent", + ("business analytics, economics, or related area", 2, 4, 1), None), + ("12200", "Full time", 70000, 128000, "Regular/Permanent", + ("operations analytics, business, or related area", 3, 5, 2), None), + ], + }, + { + "title": "Merchandising Intern", + "area": "students", + "category": "Internship", + "shifts": "WD", + "summary": "A paid summer internship inside a buying office, owning a real category project.", + "do": [ + "Merchandising Interns at {location_name} in {city}, {state} join a buying team for the summer " + "and own one category project end to end, from the data pull to the recommendation.", + "You will sit in supplier meetings, walk buildings with the field team, and present your " + "recommendation to merchandising leadership at the end of the program.", + ], + "about_team": "The internship program places students directly on the teams that make the " + "decisions.", + "min_qual_option1": "Option 1: Currently enrolled in a bachelor's degree program in {degree_field} " + "with an expected graduation date within {y1} years.", + "min_qual_option2": "Option 2: Currently enrolled in a master's degree program in {degree_field} " + "with an expected graduation date within {y2} years.", + "preferred": "Coursework or prior internship experience in retail merchandising within the last " + "{yp} years.", + "placements": [ + ("11109", "Intern", 64000, 90000, "Intern (Fixed Term)", + ("business, marketing, or supply chain", 2, 1, 2), None), + ("10101", "Intern", 66000, 92000, "Intern (Fixed Term)", + ("business administration or merchandising", 2, 1, 1), None), + ("11500", "Intern", 62000, 88000, "Intern (Fixed Term)", + ("business, merchandising, or analytics", 1, 2, 1), None), + ], + }, + { + "title": "Software Engineering Intern", + "area": "students", + "category": "Internship", + "shifts": "WD", + "summary": "A paid summer internship writing production code on a platform team.", + "do": [ + "Software Engineering Interns at {location_name} in {city}, {state} join a platform team, take " + "a real ticket in week one, and ship code to production before the summer is over.", + "You will have an engineering mentor, take part in code review, and present your project at " + "the end of the program.", + ], + "about_team": "Interns join the same teams and the same rituals as full-time engineers.", + "min_qual_option1": "Option 1: Currently enrolled in a bachelor's degree program in {degree_field} " + "with an expected graduation date within {y1} years.", + "min_qual_option2": "Option 2: Currently enrolled in a master's degree program in {degree_field} " + "with an expected graduation date within {y2} years.", + "preferred": "Coursework in data structures and algorithms and {yp} prior software internship.", + "placements": [ + ("11807", "Intern", 78000, 104000, "Intern (Fixed Term)", + ("computer science or computer engineering", 2, 1, 1), None), + ("10101", "Intern", 72000, 98000, "Intern (Fixed Term)", + ("computer science or information systems", 1, 2, 1), None), + ("11003", "Intern", 76000, 102000, "Intern (Fixed Term)", + ("software engineering or computer engineering", 3, 2, 1), None), + ], + }, + { + "title": "Finance Intern", + "area": "students", + "category": "Internship", + "shifts": "WD", + "summary": "A paid summer internship on a finance planning team.", + "do": [ + "Finance Interns at {location_name} in {city}, {state} support a planning team through a full " + "forecast cycle and own one analysis that goes in front of a business leader.", + "You will learn the planning system, the reporting stack and how the company actually decides " + "where money goes.", + ], + "about_team": "Finance interns sit with the teams they support, not in a separate cohort room.", + "min_qual_option1": "Option 1: Currently enrolled in a bachelor's degree program in {degree_field} " + "with an expected graduation date within {y1} years.", + "min_qual_option2": "Option 2: Currently enrolled in a master's degree program in {degree_field} " + "with an expected graduation date within {y2} years.", + "preferred": "Coursework in financial modelling and {yp} prior finance internship.", + "placements": [ + ("10101", "Intern", 60000, 84000, "Intern (Fixed Term)", + ("finance, accounting, or economics", 2, 1, 1), None), + ("11500", "Intern", 58000, 82000, "Intern (Fixed Term)", + ("accounting or business administration", 1, 2, 1), None), + ], + }, + { + "title": "Data Analytics Intern", + "area": "students", + "category": "Internship", + "shifts": "WD", + "summary": "A paid summer internship on an analytics team supporting eCommerce.", + "do": [ + "Data Analytics Interns at {location_name} in {city}, {state} join an analytics team, build a " + "dashboard or model that a business owner asked for, and hand it over working.", + "You will use SQL and Python daily and present your findings at the end of the program.", + ], + "about_team": "Analytics here reports into the product organisation it supports.", + "min_qual_option1": "Option 1: Currently enrolled in a bachelor's degree program in {degree_field} " + "with an expected graduation date within {y1} years.", + "min_qual_option2": "Option 2: Currently enrolled in a master's degree program in {degree_field} " + "with an expected graduation date within {y2} years.", + "preferred": "Coursework in statistics or machine learning and {yp} prior analytics internship.", + "placements": [ + ("11003", "Intern", 70000, 96000, "Intern (Fixed Term)", + ("statistics, data science, or economics", 2, 1, 1), None), + ("11807", "Intern", 74000, 100000, "Intern (Fixed Term)", + ("computer science, statistics, or mathematics", 1, 3, 1), None), + ], + }, +] + + +# --------------------------------------------------------------------------- # +# Hub copy for the four is_hub offices, keyed by store number: +# (display name, blurb, image file). Seeded onto Store.hub_name / hub_blurb / +# hub_image so the locations and career-area templates read it from the DB. +# --------------------------------------------------------------------------- # +HUB_COPY = { + "10101": ( + "Northwest Arkansas", + "Northwest Arkansas offers trails, local eats, and the Crystal Bridges Museum - while our " + "12 new Home Office buildings reflect the company's story through thoughtful design.", + "loc-nwa.jpg", + ), + "11807": ( + "Sunnyvale", + "A weekend hike through the mountains. An evening walk next to the ocean. A quick visit to a " + "museum. The best of both worlds - work and leisure - are waiting for you right here.", + "loc-sunnyvale.jpg", + ), + "11003": ( + "Hoboken", + "Just across from Lower Manhattan, Hoboken is a walkable, character-filled town on the Hudson " + "with a truly unique charm.", + "loc-hoboken.jpg", + ), + "11500": ( + "Dallas", + "Our Dallas office anchors merchandising, finance and supply chain teams in the middle of one " + "of the fastest-growing metros in the country.", + "loc-dallas.jpg", + ), +} + + +# --------------------------------------------------------------------------- # +# Job ids surfaced as "Trending roles" on the home page, the hiring page and the +# logged-out saved-roles page. Seeded onto Job.is_trending, which is what the +# handlers query. These three placements pin their job_id in `extras` so a +# catalog edit cannot silently point this list at a different posting. +# --------------------------------------------------------------------------- # +TRENDING_JOB_IDS = [ + "R-2418512", + "R-2417063", + "CP-1236-10741", +] + + +# --------------------------------------------------------------------------- # +# "What you'll bring" bullets for salaried postings, one template list per +# category. Slot fills are location only ({city}, {state}, {location_name}); the +# bullets deliberately never mention a degree or a number of years, which live +# solely in the Minimum Qualifications block. +# --------------------------------------------------------------------------- # +SALARIED_BRING = { + "Software Engineering and Architecture": [ + "A track record of designing, building and operating production services that hold up at retail traffic.", + "Fluency in at least one modern backend language and its ecosystem, plus comfort reading code in others.", + "Hands-on experience with distributed data stores, message queues and cloud infrastructure.", + "A test-driven approach to development and a strong commitment to code quality and documentation.", + "Clear written and spoken communication with engineers, product managers and partners across {city}.", + ], + "Product Management": [ + "Experience owning a product area end to end, from discovery through launch and iteration.", + "The ability to turn ambiguous customer problems into a crisp roadmap and measurable outcomes.", + "Comfort working daily with engineering, design and data science partners in {city}.", + "Strong written communication, including product specs and executive updates.", + ], + "Data Science and Analytics": [ + "Hands-on experience building and shipping statistical or machine learning models in production.", + "Fluency in Python or R and SQL, and comfort working with very large datasets.", + "The judgment to know when a simple model beats a complex one.", + "Experience explaining findings to non-technical partners across the {city} office.", + ], + "Information Security": [ + "Deep familiarity with threat modeling, secure design review and incident response.", + "Experience with identity, access management and cloud security controls at scale.", + "The ability to translate risk into priorities that engineering teams can act on.", + "Calm, clear communication during live incidents.", + ], + "Creative Design and UX": [ + "A portfolio that shows end-to-end design work, from research through shipped experience.", + "Fluency in modern design and prototyping tools and a working knowledge of front-end constraints.", + "Experience planning and running user research and turning it into design decisions.", + "The ability to present and defend design decisions to partners in {city}.", + ], + "Technical Program Management": [ + "Experience running large cross-functional programs with many engineering teams.", + "Enough technical depth to challenge estimates and spot dependencies early.", + "A bias for clear plans, visible risks and honest status.", + "Strong facilitation skills across the {city} office and remote partners.", + ], + "Information Technology": [ + "Experience supporting enterprise endpoints, identity systems and collaboration tools.", + "Scripting skills for automating repetitive support and provisioning tasks.", + "A customer-first approach to troubleshooting and a habit of documenting fixes.", + "Comfort supporting associates on site in {city} and remotely.", + ], + "Accounting and Finance": [ + "Experience owning forecasts, budgets or close processes for a large business unit.", + "Advanced spreadsheet and financial modeling skills, plus comfort with planning systems.", + "The ability to explain variances to operators and executives in plain language.", + "Attention to detail and a strong sense of ownership over the numbers.", + ], + "Human Resources": [ + "Experience partnering with leaders on talent, organization design and associate relations.", + "Working knowledge of employment practices and the judgment to apply them fairly.", + "Strong coaching and facilitation skills.", + "Comfort supporting teams across the {city} office and the field.", + ], + "Marketing and Advertising": [ + "Experience planning and running integrated campaigns across digital and in-store channels.", + "Fluency in campaign measurement and the ability to act on what the data says.", + "Strong creative judgment and clear briefing skills for agency and in-house partners.", + "Comfort presenting plans and results to senior leaders in {city}.", + ], + "Merchandising": [ + "Experience owning assortment, pricing or replenishment decisions for a category.", + "Strong analytical skills and comfort working in large planning and forecasting systems.", + "The ability to negotiate with suppliers and build long-term partnerships.", + "A customer-first mindset and a habit of walking the stores.", + ], + "Business Operations": [ + "Experience owning operational metrics and the processes behind them.", + "Strong analytical skills, including the ability to build and interpret operational dashboards.", + "Comfort working across product, data science and field operations partners.", + "A habit of spending time where the work happens, not only in the model.", + ], + "Internship": [ + "Current enrollment in a degree program with an expected graduation date after the internship term.", + "Curiosity about how a large retailer runs and a willingness to ask questions.", + "Comfort working in a team and presenting your project to leaders in {city}.", + "Availability for the full internship term.", + ], +} diff --git a/sites/walmart_careers/requirements.txt b/sites/walmart_careers/requirements.txt new file mode 100644 index 00000000..0ecb02b7 --- /dev/null +++ b/sites/walmart_careers/requirements.txt @@ -0,0 +1,5 @@ +Flask +Flask-SQLAlchemy +Flask-Login +Flask-WTF +Werkzeug diff --git a/sites/walmart_careers/seed_data.py b/sites/walmart_careers/seed_data.py new file mode 100644 index 00000000..73ae0fab --- /dev/null +++ b/sites/walmart_careers/seed_data.py @@ -0,0 +1,464 @@ +"""Deterministic seed for the Walmart Careers mirror. + +Run directly (`PYTHONHASHSEED=0 python seed_data.py`) to rebuild +`instance_seed/walmart_careers.db` from `catalog_source.py`. The build is +byte-reproducible: one RNG, no wall-clock reads, sorted iteration only, and +werkzeug password hashes hard-coded because werkzeug salts randomly. +""" +from __future__ import annotations + +import importlib.util +import json +import os +import random +import shutil +from datetime import date, datetime, timedelta +from pathlib import Path + +os.environ.setdefault("WEBSYN_SKIP_BOOTSTRAP", "1") + +import catalog_source as source +from _content import MIRROR_REFERENCE_DATE +from app import ( + Application, + Area, + Category, + Job, + SavedJob, + Store, + User, + app, + confirmation_for, + db, + dumps_json, +) + +RNG = random.Random(20260905) +BASE_DIR = Path(__file__).resolve().parent +DB_PATH = BASE_DIR / "instance" / "walmart_careers.db" +INSTANCE_SEED_DIR = BASE_DIR / "instance_seed" + +# Hard-coded werkzeug hashes of DEMO_PASSWORD ("TestPass123!"). generate_password_hash +# salts randomly, so recomputing them here would break byte-identical rebuilds. +DEMO_PASSWORD_HASHES = { + "alice.j@test.com": "scrypt:32768:8:1$x9JMG7iKsrRO1AGh$e8e195799326a6e1ff55d4d20dd2735d9d68c0ed4879bd6b263ac33fa9953b0f6c8505680f1a1d8a9fbe9b0d01ef5e88f99b77b89fc30299fda217185a3b7acf", + "bob.c@test.com": "scrypt:32768:8:1$O14LIVdpqb3Q6D7E$4b9389cd00aad4417058fd629af5bf979b3f05bdf011791fbc65b7080fe898e50c7aedc2c22be92c71ae25a1df6922bb4ca44b386a7f17040b36888c0cdc8942", + "carol.d@test.com": "scrypt:32768:8:1$9PGtFS6I89BOEugS$890b1c1935bb6c0c4a6f7f5ad689cc02415e4bd03b02e101f0c2095931d4f157a8504c0fa4f12c3073c94e1480fea3305ffbadc5e8540c5eaf1c16965cb47be7", + "david.k@test.com": "scrypt:32768:8:1$luqs3gbiT1hPpw2c$09f31fef9514ae90d234cdd91a7f2c95d937e08a37fed60ce0b41755a097609040f7aa5083b94ecdd41804262dc778bd6f8d8e9f38f47f319fa8d6f3ed705d1b", +} + +BENCHMARK_USERS = [ + ("alice.j@test.com", "alice.j", "Alice Johnson", "Alice", "Johnson", "479-555-0134", "Bentonville", "AR"), + ("bob.c@test.com", "bob.c", "Bob Chen", "Bob", "Chen", "206-555-0178", "Seattle", "WA"), + ("carol.d@test.com", "carol.d", "Carol Davis", "Carol", "Davis", "253-555-0119", "Tacoma", "WA"), + ("david.k@test.com", "david.k", "David Kim", "David", "Kim", "214-555-0166", "Dallas", "TX"), +] +USER_CREATED_AT = datetime(2026, 6, 12, 9, 30, 0) + +# (user email, job title, store number) — resolved to job ids after the catalog is built. +SEED_SAVED_JOBS = [ + ("alice.j@test.com", "Freight Handler", "9046", datetime(2026, 8, 3, 14, 12, 0)), + ("alice.j@test.com", "Cosmetics Cashier", "2503", datetime(2026, 8, 7, 19, 41, 0)), + ("alice.j@test.com", "Optician", "5991", datetime(2026, 8, 9, 10, 5, 0)), + ("alice.j@test.com", "Automation Technician", "6088", datetime(2026, 8, 13, 8, 16, 0)), + ("alice.j@test.com", "Senior UX Designer", "11807", datetime(2026, 8, 17, 12, 27, 0)), + ("alice.j@test.com", "Team Lead", "4137", datetime(2026, 8, 22, 17, 58, 0)), + ("bob.c@test.com", "Asset Protection Associate", "5991", datetime(2026, 8, 4, 8, 22, 0)), + ("bob.c@test.com", "Class A CDL Truck Driver", "6038", datetime(2026, 8, 11, 16, 48, 0)), + ("bob.c@test.com", "Senior Data Scientist", "11500", datetime(2026, 8, 20, 12, 3, 0)), + ("carol.d@test.com", "Pharmacy Technician", "4137", datetime(2026, 8, 6, 7, 55, 0)), + ("carol.d@test.com", "Cafe Associate", "6318", datetime(2026, 8, 14, 21, 17, 0)), + ("david.k@test.com", "Merchandising and Stocking Associate", "4750", datetime(2026, 8, 8, 11, 26, 0)), + ("david.k@test.com", "IT Support Engineer", "10101", datetime(2026, 8, 19, 15, 34, 0)), +] + +# (user email, job title, store number, submitted_at) +SEED_APPLICATIONS = [ + ("alice.j@test.com", "Team Lead", "144", datetime(2026, 8, 5, 13, 20, 0)), + ("bob.c@test.com", "Order Filler", "6014", datetime(2026, 8, 12, 9, 2, 0)), + ("carol.d@test.com", "Optician", "2073", datetime(2026, 8, 16, 17, 44, 0)), + ("david.k@test.com", "Financial Analyst III", "11500", datetime(2026, 8, 21, 10, 11, 0)), +] + +HERO_SETS = { + "salaried": ["jobhero-corp-1.jpg", "jobhero-corp-2.jpg", "jobhero-corp-3.jpg"], + "sams": ["jobhero-sams-1.png", "jobhero-sams-2.jpg", "jobhero-wm-2.jpg"], + "walmart": ["jobhero-wm-1.png", "jobhero-wm-3.jpg", "jobhero-wm-4.jpg"], + "walmart-alt": ["jobhero-wm-4.jpg", "jobhero-wm-2.jpg", "jobhero-wm-1.png"], +} + +HOURLY_CLOSING = ( + "At Walmart, we offer competitive pay as well as performance-based incentive awards and other " + "great benefits for a happier mind, body, and wallet. Health benefits include medical, vision and " + "dental coverage. Financial benefits include 401(k), stock purchase and company-paid life " + "insurance. Paid time off benefits include parental leave, family care leave, bereavement, jury " + "duty, and voting." +) +LBU_CLOSING = ( + "Live Better U is a Walmart-paid education benefit program for full-time and part-time associates " + "in Walmart and Sam's Club facilities. Programs range from high school completion to bachelor's " + "degrees, including English Language Learning and short-form certificates. Tuition, books, and " + "fees are completely paid for by Walmart." +) + + +# --------------------------------------------------------------------------- # +# Catalog construction +# --------------------------------------------------------------------------- # +def _shift_names(codes: str) -> list[str]: + return [source.SHIFT_CODES[c] for c in codes.split(",")] + + +def _build_areas() -> dict[str, Area]: + areas: dict[str, Area] = {} + for slug, name, order, blurb, hero, has_index, filterable in source.AREAS: + area = Area( + slug=slug, + name=name, + display_order=order, + blurb=blurb, + hero_image=hero, + has_index_page=has_index, + is_filterable=filterable, + ) + db.session.add(area) + areas[slug] = area + db.session.flush() + return areas + + +def _build_categories(areas: dict[str, Area]) -> dict[tuple[str, str], Category]: + categories: dict[tuple[str, str], Category] = {} + for area_slug, name, slug, order in source.CATEGORIES: + category = Category( + area_id=areas[area_slug].id, name=name, slug=slug, display_order=order + ) + db.session.add(category) + categories[(area_slug, name)] = category + db.session.flush() + return categories + + +def _build_stores() -> dict[str, Store]: + stores: dict[str, Store] = {} + for row in source.STORES: + (number, banner, location_name, street, city, state, zip_code, + lat, lng, is_hub, is_office, _brand) = row + hub_name, hub_blurb, hub_image = source.HUB_COPY.get(number, (None, None, None)) + store = Store( + store_number=number, + banner=banner, + location_name=location_name, + street=street, + city=city, + state=state, + zip=zip_code, + lat=lat, + lng=lng, + is_hub=is_hub, + is_office=is_office, + hub_name=hub_name, + hub_blurb=hub_blurb, + hub_image=hub_image, + ) + db.session.add(store) + stores[number] = store + db.session.flush() + return stores + + +def _store_brand() -> dict[str, str]: + return {row[0]: row[11] for row in source.STORES} + + +def _hero_for(population: str, brand: str, index: int) -> list[str]: + if population == "salaried": + pool = HERO_SETS["salaried"] + elif brand == "Sam's Club": + pool = HERO_SETS["sams"] + elif index % 2: + pool = HERO_SETS["walmart-alt"] + else: + pool = HERO_SETS["walmart"] + return list(pool) + + +def _build_jobs(areas, categories, stores) -> list[Job]: + brands = _store_brand() + trending = set(source.TRENDING_JOB_IDS) + used_ids: set[str] = set() + for family in source.HOURLY_FAMILIES: + for placement in family["placements"]: + extras = placement[6] or {} + if "job_id" in extras: + used_ids.add(extras["job_id"]) + for family in source.SALARIED_FAMILIES: + for placement in family["placements"]: + extras = placement[6] or {} + if "job_id" in extras: + used_ids.add(extras["job_id"]) + + jobs: list[Job] = [] + store_cursor: dict[str, int] = {} + index = 0 + + for family in source.HOURLY_FAMILIES: + area = areas[family["area"]] + category = categories[(family["area"], family["category"])] + for placement in family["placements"]: + store_no, emp_type, codes, min_pay, max_pay, positions, extras = placement + extras = extras or {} + store = stores[store_no] + brand = brands[store_no] + cursor = store_cursor.get(store_no, 10200) + RNG.randint(120, 980) + job_id = extras.get("job_id") + if job_id is None: + job_id = f"CP-{store_no}-{cursor}" + while job_id in used_ids: + cursor += 37 + job_id = f"CP-{store_no}-{cursor}" + store_cursor[store_no] = cursor + used_ids.add(job_id) + + fmt = { + "banner": store.banner, + "store": store.store_number, + "city": store.city, + "state": store.state, + "location_name": store.location_name, + } + paragraphs = [p.format(**fmt) for p in family["do"]] + paragraphs.append(HOURLY_CLOSING) + paragraphs.append(LBU_CLOSING) + primary_code = codes.split(",")[0] + job = Job( + job_id=job_id, + population="hourly", + title=family["title"], + brand=brand, + store_id=store.id, + area_id=area.id, + category_id=category.id, + shifts_json=dumps_json(_shift_names(codes)), + employment_type=emp_type, + pay_frequency="Hourly", + min_pay=min_pay, + max_pay=max_pay, + posted_date=MIRROR_REFERENCE_DATE - timedelta(days=RNG.randint(1, 120)), + sort_rank=0, + is_trending=job_id in trending, + summary=family["summary"].format(**fmt), + description="\n\n".join(paragraphs), + additional_description_json=dumps_json( + [b.format(**fmt) for b in family["bring"]] + ), + hashtag=family.get("hashtag"), + shift_time=extras.get("shift_time", source.SHIFT_WINDOWS[primary_code]), + positions_available=positions, + min_age_note=emp_type != "Intern", + hero_images_json=dumps_json(_hero_for("hourly", brand, index)), + ) + db.session.add(job) + jobs.append(job) + index += 1 + + salaried_cursor = 2410000 + posting_seq = 5210000 + for family in source.SALARIED_FAMILIES: + area = areas[family["area"]] + category = categories[(family["area"], family["category"])] + for placement in family["placements"]: + store_no, emp_type, min_pay, max_pay, worker_type, slots, extras = placement + extras = extras or {} + store = stores[store_no] + brand = brands[store_no] + salaried_cursor += RNG.randint(150, 900) + job_id = extras.get("job_id") + if job_id is None: + job_id = f"R-{salaried_cursor}" + while job_id in used_ids: + salaried_cursor += 13 + job_id = f"R-{salaried_cursor}" + used_ids.add(job_id) + posting_seq += RNG.randint(400, 4000) + + degree_field, y1, y2, yp = slots + fmt = { + "banner": store.banner, + "store": store.store_number, + "city": store.city, + "state": store.state, + "location_name": store.location_name, + "degree_field": degree_field, + "y1": y1, + "y2": y2, + "yp": yp, + } + paragraphs = [p.format(**fmt) for p in family["do"]] + clause = extras.get("qual_clause") + + def qualification(template: str) -> str: + text = template.format(**fmt) + if clause: + text = text.rstrip(".") + ", " + clause + "." + return text + + bring = [b.format(**fmt) for b in source.SALARIED_BRING[family["category"]]] + job = Job( + job_id=job_id, + population="salaried", + title=family["title"], + brand=brand, + store_id=store.id, + area_id=area.id, + category_id=category.id, + shifts_json=dumps_json(_shift_names(family["shifts"])), + employment_type=emp_type, + pay_frequency="Annual", + min_pay=min_pay, + max_pay=max_pay, + posted_date=MIRROR_REFERENCE_DATE - timedelta(days=RNG.randint(1, 120)), + sort_rank=0, + is_trending=job_id in trending, + summary=family["summary"].format(**fmt), + description="\n\n".join(paragraphs), + about_team=family["about_team"].format(**fmt), + additional_description_json=dumps_json(bring), + hashtag=None, + shift_time=None, + positions_available=None, + min_age_note=False, + worker_type=worker_type, + job_posting_id=f"JOB_POSTING-3-{posting_seq}", + min_qualifications_json=dumps_json( + [ + qualification(family["min_qual_option1"]), + qualification(family["min_qual_option2"]), + ] + ), + preferred_qualifications=family["preferred"].format(**fmt), + hero_images_json=dumps_json(_hero_for("salaried", brand, index)), + ) + db.session.add(job) + jobs.append(job) + index += 1 + + ranks = list(range(len(jobs))) + RNG.shuffle(ranks) + for job, rank in zip(jobs, ranks): + job.sort_rank = rank + db.session.flush() + return jobs + + +# --------------------------------------------------------------------------- # +# Seed entry points (each gated as a whole — see AGENTS.md "Idempotent seeding") +# --------------------------------------------------------------------------- # +def seed_database(force: bool = False) -> None: + if Job.query.count() > 0 and not force: + return + areas = _build_areas() + categories = _build_categories(areas) + stores = _build_stores() + _build_jobs(areas, categories, stores) + db.session.commit() + + +def seed_benchmark_users(force: bool = False) -> None: + if User.query.filter_by(email="alice.j@test.com").first() and not force: + return + users: dict[str, User] = {} + for email, username, display, first, last, phone, city, state in BENCHMARK_USERS: + user = User( + email=email, + username=username, + display_name=display, + first_name=first, + last_name=last, + phone=phone, + city=city, + state=state, + password_hash=DEMO_PASSWORD_HASHES[email], + created_at=USER_CREATED_AT, + ) + db.session.add(user) + users[email] = user + db.session.flush() + + for email, title, store_number, saved_at in SEED_SAVED_JOBS: + job = _find_job(title, store_number) + db.session.add(SavedJob(user_id=users[email].id, job_id=job.job_id, saved_at=saved_at)) + + for email, title, store_number, submitted_at in SEED_APPLICATIONS: + job = _find_job(title, store_number) + user = users[email] + application = Application( + job_id=job.job_id, + user_id=user.id, + email=user.email, + first_name=user.first_name, + last_name=user.last_name, + phone=user.phone, + status="Submitted", + confirmation_no="pending", + submitted_at=submitted_at, + ) + db.session.add(application) + db.session.flush() + application.confirmation_no = confirmation_for(application.id) + + db.session.commit() + + +def _find_job(title: str, store_number: str) -> Job: + store = Store.query.filter_by(store_number=store_number).one() + job = ( + Job.query.filter_by(title=title, store_id=store.id) + .order_by(Job.job_id) + .first() + ) + if job is None: + raise RuntimeError(f"no seeded job {title!r} at store {store_number}") + return job + + +# --------------------------------------------------------------------------- # +# Build-time invariant checks. +# +# The benchmark task locators and their expected answers live in +# scripts_dev/assert_distractors.py, which is git-ignored and docker-ignored and +# therefore absent from the shipped tree. The freezer loads it by path when it is +# there, so a developer rebuild still fails on a catalog edit that breaks a task; +# a tree without it builds the same database and skips the checks. +# --------------------------------------------------------------------------- # +def _load_distractor_checks(): + path = BASE_DIR / "scripts_dev" / "assert_distractors.py" + if not path.exists(): + return None + spec = importlib.util.spec_from_file_location("walmart_careers_assert_distractors", path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module.assert_distractors + + +def build_seed_database() -> None: + INSTANCE_SEED_DIR.mkdir(parents=True, exist_ok=True) + DB_PATH.parent.mkdir(parents=True, exist_ok=True) + if DB_PATH.exists(): + DB_PATH.unlink() + checks = _load_distractor_checks() + with app.app_context(): + db.drop_all() + db.create_all() + seed_database(force=True) + seed_benchmark_users(force=True) + if checks is not None: + checks() + shutil.copyfile(DB_PATH, INSTANCE_SEED_DIR / "walmart_careers.db") + if checks is None: + print("scripts_dev/assert_distractors.py not present - build-time invariants skipped.") + + +if __name__ == "__main__": + build_seed_database() + print("Seed database generated from the deterministic Walmart Careers source catalog.") diff --git a/sites/walmart_careers/static/css/site.css b/sites/walmart_careers/static/css/site.css new file mode 100644 index 00000000..79a51452 --- /dev/null +++ b/sites/walmart_careers/static/css/site.css @@ -0,0 +1,865 @@ +/* Walmart Careers mirror — Living Design tokens pulled from the live site CSS. */ +@font-face { + font-family: "EverydaySansUI"; + src: url("../fonts/EverydaySansUI-wght.ttf") format("truetype-variations"); + font-weight: 100 900; + font-display: swap; +} + +:root { + --ld-blue-100: #0053e2; + --ld-blue-130: #002e99; + --ld-blue-160: #001e60; + --ld-blue-10: #e6f1fc; + --ld-blue-9: #e9f1fe; + --ld-sky: #a9ddf7; + --ld-sky-60: #4dbdf5; + --ld-spark-100: #ffc220; + --ld-spark-30: #ffe4a3; + --ld-gray-200: #f1f1f2; + --ld-gray-20: #e3e4e5; + --ld-gray-5: #f8f8f8; + --ld-gray-100: #74767c; + --ld-text-subtle: #515357; + --ld-red: #de1c24; + --ld-green: #2a8703; + --ld-sams: #00358e; + --header-h: 80px; + --gutter: 140px; +} + +* { box-sizing: border-box; } + +html { scroll-behavior: smooth; } + +body { + margin: 0; + font-family: "EverydaySansUI", "Bogle", Arial, Helvetica, sans-serif; + font-size: 16px; + line-height: 1.45; + color: var(--ld-blue-160); + background: #fff; +} + +a { color: var(--ld-blue-100); } +a:hover { color: var(--ld-blue-130); } + +img { max-width: 100%; } + +.skip-link { + position: absolute; left: -9999px; top: 0; background: #fff; color: var(--ld-blue-160); + padding: 8px 16px; z-index: 100; +} +.skip-link:focus { left: 8px; top: 8px; } + +.wrap { max-width: 1440px; margin: 0 auto; padding: 0 32px; } +.l1 .wrap { padding: 0 var(--gutter); } +.wrap-narrow { max-width: 900px; margin: 0 auto; padding: 0 32px; } +.wrap-bleed { max-width: 1440px; margin: 0 auto; padding: 0 24px; } + +/* ------------------------------ header ---------------------------------- */ +.site-header { + background: var(--ld-blue-100); + min-height: var(--header-h); + display: flex; align-items: center; + position: sticky; top: 0; z-index: 60; +} +.site-header .wrap { + display: flex; align-items: center; gap: 20px; width: 100%; flex-wrap: nowrap; + max-width: none; padding: 0 24px; +} +.brand { display: flex; align-items: center; flex: 0 0 auto; text-decoration: none; } +.brand img { height: 38px; width: auto; } + +.main-nav { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; white-space: nowrap; } +.main-nav .nav-link, .nav-menu > summary { + color: #fff; text-decoration: none; font-size: 16px; padding: 10px 12px; + border-radius: 8px; display: inline-flex; align-items: center; gap: 6px; + cursor: pointer; list-style: none; white-space: nowrap; +} +.nav-menu > summary::-webkit-details-marker { display: none; } +.nav-menu > summary::marker { content: ""; } +.main-nav .nav-link:hover, .nav-menu > summary:hover, +.nav-menu[open] > summary { background: var(--ld-blue-130); color: #fff; } + +.nav-menu { position: relative; } +.nav-pop { + position: absolute; top: calc(100% + 10px); left: 0; z-index: 70; + background: #fff; border-radius: 16px; padding: 12px 8px; min-width: 268px; + box-shadow: 0 8px 28px rgba(0, 30, 96, .22); display: flex; flex-direction: column; +} +.nav-pop a, .nav-pop .linkish { + color: var(--ld-blue-160); text-decoration: none; padding: 9px 16px; border-radius: 10px; + font-size: 15px; text-align: left; white-space: nowrap; +} +.nav-pop a:hover, .nav-pop .linkish:hover { background: var(--ld-blue-9); color: var(--ld-blue-160); } +.nav-pop hr { border: 0; border-top: 1px solid var(--ld-gray-20); margin: 8px 12px; width: calc(100% - 24px); } +.nav-pop .lang { + padding: 9px 16px; font-size: 14px; color: var(--ld-blue-160); + display: inline-flex; align-items: center; gap: 8px; +} +.nav-pop .lang svg { color: var(--ld-blue-100); } + +.header-search { display: flex; align-items: center; flex: 1 1 auto; justify-content: flex-end; } +.header-spacer { flex: 1 1 auto; } +.header-search form { + display: flex; align-items: center; background: #fff; + border-radius: 999px; padding: 5px 5px 5px 22px; + width: 100%; max-width: 448px; +} +.header-search input { + border: 0; outline: none; flex: 1; min-width: 0; font-size: 15px; font-family: inherit; + color: var(--ld-blue-160); background: transparent; +} +.header-search input::placeholder { color: var(--ld-blue-160); } +.header-search button { + border: 0; background: var(--ld-blue-100); width: 38px; height: 38px; + border-radius: 999px; cursor: pointer; display: grid; place-items: center; +} +.header-search button svg { color: #fff; } + +.user-menu { flex: 0 0 auto; position: relative; } +.user-menu > summary { + list-style: none; cursor: pointer; color: #fff; display: grid; place-items: center; + width: 40px; height: 40px; border-radius: 999px; +} +.user-menu > summary::-webkit-details-marker { display: none; } +.user-menu > summary::marker { content: ""; } +.user-menu[open] > summary, .user-menu > summary:hover { background: var(--ld-blue-130); } +.user-menu .nav-pop { left: auto; right: 0; min-width: 128px; padding: 14px 8px; top: calc(100% - 8px); } +.user-menu .nav-pop a, .user-menu .nav-pop .linkish { font-size: 14px; padding: 8px 12px; } +.avatar { + width: 34px; height: 34px; border-radius: 999px; background: var(--ld-spark-100); + color: var(--ld-blue-160); display: grid; place-items: center; font-weight: 700; font-size: 14px; +} +.linkish { + background: none; border: 0; font: inherit; cursor: pointer; padding: 0; + color: var(--ld-blue-160); +} + +/* ------------------------------ buttons --------------------------------- */ +.btn { + display: inline-block; border: 0; border-radius: 999px; cursor: pointer; + font: inherit; font-weight: 700; padding: 12px 24px; text-decoration: none; + background: var(--ld-blue-100); color: #fff; +} +.btn:hover { background: var(--ld-blue-130); color: #fff; } +.btn-secondary { background: #fff; color: var(--ld-blue-160); border: 1px solid var(--ld-blue-160); } +.btn-secondary:hover { background: var(--ld-gray-200); color: var(--ld-blue-160); } +.btn-spark { background: var(--ld-spark-100); color: var(--ld-blue-160); } +.btn-spark:hover { background: #ffd45c; color: var(--ld-blue-160); } +.btn-sm { padding: 7px 18px; font-size: 14px; } +.btn-block { display: block; width: 100%; text-align: center; padding: 14px 24px; } + +/* ------------------------------ hero ------------------------------------ */ +.hero { background: var(--ld-blue-100); color: #fff; padding: 0; position: relative; } +.hero .inner { max-width: 932px; margin: 0 auto; padding: 84px 0 300px; text-align: left; } +.hero h1 { font-size: 92px; line-height: 1.18; font-weight: 300; margin: 0 0 56px; } +.hero h1 span { display: block; } +.hero-search { + display: flex; align-items: center; background: var(--ld-blue-130); + border-radius: 999px; padding: 22px 22px 22px 40px; margin: 0; +} +.hero-search input { + flex: 1; min-width: 0; border: 0; outline: none; font-size: 24px; font-family: inherit; + color: #fff; background: transparent; font-weight: 300; +} +.hero-search input::placeholder { color: #fff; } +.hero-search button { + border: 0; background: #fff; color: var(--ld-blue-100); border-radius: 999px; + width: 68px; height: 68px; display: grid; place-items: center; cursor: pointer; +} +.hero-strip { + position: absolute; left: 0; right: 0; bottom: -232px; + display: grid; grid-template-columns: 330px 1fr 330px; gap: 46px; align-items: end; +} +.hero-strip > img, .hero-strip-mid img { + display: block; width: 100%; height: 380px; object-fit: cover; border-radius: 20px; +} +.hero-strip > img:first-child { border-radius: 0 20px 20px 0; } +.hero-strip > img:last-child { border-radius: 20px 0 0 20px; } +.hero-strip-mid { position: relative; } +.hero-pill { + position: absolute; right: 62px; bottom: 76px; + background: var(--ld-blue-100); color: #fff; border-color: var(--ld-blue-100); + font-weight: 400; font-size: 15px; padding: 10px 24px; +} +.hero-pill:hover { background: var(--ld-blue-130); color: #fff; } +.after-hero { padding-top: 400px; } + +/* ------------------------------ sections -------------------------------- */ +section { padding: 56px 0; } +section h2 { font-size: 40px; font-weight: 300; margin: 0 0 32px; line-height: 1.15; } +section h2.tight { margin-bottom: 12px; } +section h3 { font-size: 22px; font-weight: 700; margin: 0 0 12px; } +.section-blurb { font-size: 15px; max-width: 680px; margin: 0 0 40px; } +.section-alt { background: var(--ld-gray-5); } +.section-blue { background: var(--ld-blue-160); color: #fff; } +.section-blue h2, .section-blue a { color: #fff; } + +/* career-area ribbon */ +.ribbon-section { padding: 90px 0 60px; } +.ribbon { display: flex; border-radius: 40px; overflow: hidden; } +.ribbon-seg { + flex: 1 1 0; display: flex; align-items: center; justify-content: space-between; gap: 12px; + padding: 44px 30px 44px 24px; text-decoration: none; font-size: 18px; font-weight: 300; + line-height: 1.3; border-radius: 40px 0 0 40px; margin-left: -40px; padding-left: 60px; +} +.ribbon-seg:first-child { margin-left: 0; padding-left: 24px; } +.ribbon-seg svg { flex: 0 0 auto; } +.seg-1 { background: var(--ld-blue-10); color: var(--ld-blue-160); z-index: 5; } +.seg-2 { background: var(--ld-sky); color: var(--ld-blue-160); z-index: 4; } +.seg-3 { background: var(--ld-blue-100); color: #fff; z-index: 3; } +.seg-4 { background: var(--ld-blue-130); color: #fff; z-index: 2; } +.seg-5 { background: var(--ld-blue-160); color: #fff; z-index: 1; } +.ribbon-seg:hover { filter: brightness(1.06); color: inherit; } + +/* bento */ +.bento-section { padding: 40px 0 70px; } +.bento-layout { display: grid; grid-template-columns: 1040px 1fr; gap: 24px; align-items: start; } +.bento { display: grid; grid-template-columns: repeat(6, 1fr); gap: 24px; } +.tile { + position: relative; border-radius: 24px; overflow: hidden; text-decoration: none; + display: block; color: var(--ld-blue-160); +} +.tile-sq { grid-column: span 2; height: 296px; } +.tile-wide { grid-column: span 4; height: 296px; } +.tile-short { grid-column: span 2; height: 150px; } +.tile-mini { grid-column: span 1; height: 150px; } +.tile-gap { grid-column: span 2; } +.tile-sky { background: var(--ld-sky); } +.tile-pale { background: var(--ld-blue-10); } +.tile-blue { background: var(--ld-blue-100); color: #fff; } +.tile-navy { background: var(--ld-blue-160); color: #fff; } +.tile-spark { background: var(--ld-spark-100); } +.tile-photo img { display: block; width: 100%; height: 100%; object-fit: cover; } +.tile-center { display: grid; place-items: center; } +.tile-label { position: absolute; left: 24px; top: 22px; right: 24px; font-size: 26px; font-weight: 300; line-height: 1.25; } +.spark-card { width: 130px; height: 130px; } +.tile-heart { width: 86px; height: 86px; } +.tile-diamond { width: 140px; height: 140px; filter: brightness(0) invert(1); } +.circle-arrow { + position: absolute; left: 24px; bottom: 22px; width: 48px; height: 48px; border-radius: 999px; + border: 1px solid currentColor; display: grid; place-items: center; font-size: 30px; line-height: 1; + font-weight: 300; +} +a.tile:hover { color: inherit; filter: brightness(1.04); } +a.tile.tile-sky:hover, a.tile.tile-pale:hover { color: var(--ld-blue-160); } +.bento-aside { padding-top: 12px; } +.bento-aside h2 { font-size: 40px; font-weight: 300; margin: 0 0 22px; } + +/* benefits */ +.benefits-layout { display: grid; grid-template-columns: 1fr 360px; gap: 60px; align-items: start; } +.benefit-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 34px 40px; } +.benefit-item img { width: 60px; height: 60px; display: block; margin-bottom: 10px; } +.benefit-item b { display: block; font-size: 13px; margin-bottom: 4px; } +.benefit-item span { font-size: 13px; color: var(--ld-blue-160); display: block; line-height: 1.45; } +.benefits-aside p { font-size: 30px; font-weight: 300; line-height: 1.35; margin: 0 0 30px; } +/* shared benefits partial (job detail + career-area pages) */ +.benefits-block { padding: 80px 0 0; } +.benefits-block h2 { font-size: 40px; font-weight: 300; margin: 0 0 32px; line-height: 1.15; } +.benefit-rows { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 28px 40px; margin-top: 30px; } +.benefit-row { display: flex; gap: 20px; align-items: flex-start; } +.benefit-row img { width: 90px; height: 90px; flex: 0 0 auto; } +.benefit-row b { display: block; font-size: 13px; margin: 4px 0 2px; } +.benefit-row span { font-size: 13px; color: var(--ld-blue-160); display: block; line-height: 1.45; } +.benefits-cta { margin: 36px 0 0; } + +/* milestones */ +.milestones { padding: 90px 0 60px; } +.milestone-layout { display: grid; grid-template-columns: 360px 1fr; gap: 20px; align-items: start; } +.milestone-layout h2 { font-size: 64px; font-weight: 300; line-height: 1.05; margin: 90px 0 0; } +.badge-stack { display: flex; flex-direction: column; gap: 110px; } +.badge-row { display: flex; align-items: flex-start; gap: 24px; } +.badge-row.row-2, .badge-row.row-4 { padding-left: 0; } +.badge-row.row-3 { padding-left: 0; } +.badge { + width: 452px; border-radius: 26px; overflow: hidden; border: 1px solid var(--ld-gray-20); + text-align: center; box-shadow: 0 1px 2px rgba(0, 30, 96, .08); background: #fff; +} +.badge-top { height: 48px; position: relative; } +.badge-top .grip { + position: absolute; left: 50%; top: 8px; transform: translateX(-50%); + width: 40px; height: 7px; border-radius: 999px; background: #fff; +} +.badge-top em { position: absolute; right: 50px; top: 18px; font-style: normal; font-size: 16px; } +.badge-body { padding: 40px 36px; font-size: 24px; line-height: 1.4; min-height: 160px; display: grid; place-items: center; } +.badge-foot { padding: 20px 0 26px; font-weight: 800; color: var(--ld-blue-100); font-size: 24px; letter-spacing: -.5px; } +.badge-foot.plain { font-weight: 400; color: var(--ld-blue-160); font-size: 16px; } +.badge-sky .badge-top { background: var(--ld-sky-60); } +.badge-sky .badge-body { background: var(--ld-blue-100); color: #fff; } +.badge-spark .badge-top { background: var(--ld-spark-100); color: var(--ld-blue-160); } +.badge-spark .badge-body { background: var(--ld-spark-30); } +.badge-navy .badge-top { background: var(--ld-blue-130); } +.badge-navy .badge-body { background: #fff; } +.badge-blue .badge-top { background: var(--ld-blue-100); color: #fff; } +.badge-blue .badge-body { background: var(--ld-blue-160); color: #fff; } +.badge-blank { width: 214px; height: 132px; border-radius: 26px; flex: 0 0 auto; } +.blank-sky { background: var(--ld-sky-60); } +.blank-spark { background: var(--ld-spark-30); } +.blank-navy { background: transparent; } +.blank-blue { background: var(--ld-blue-100); } + +/* video strip */ +.video-strip { display: grid; grid-template-columns: 300px 1fr 300px; gap: 24px; } +.video-tile { position: relative; height: 386px; border-radius: 24px; overflow: hidden; background: var(--ld-blue-100); color: #fff; } +.video-tile > img.v-photo { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: .55; } +.video-strip.dark .video-tile { background: #000; } +.v-spark { position: absolute; left: 50%; top: 150px; transform: translateX(-50%); width: 76px; height: 76px; } +.v-cap { position: absolute; left: 24px; bottom: 26px; } +.v-cap em { display: block; font-style: normal; font-size: 14px; } +.v-cap b { display: block; font-size: 20px; margin: 4px 0 18px; } +.play { + display: block; width: 56px; height: 56px; border-radius: 999px; background: #fff; position: relative; +} +.play::after { + content: ""; position: absolute; left: 21px; top: 16px; border-left: 20px solid var(--ld-blue-100); + border-top: 12px solid transparent; border-bottom: 12px solid transparent; +} +.dots { display: flex; justify-content: center; gap: 12px; margin-top: 34px; } +.dots i { width: 12px; height: 12px; border-radius: 999px; border: 1.5px solid var(--ld-blue-160); display: block; } +.dots i.on { width: 100px; background: linear-gradient(90deg, var(--ld-blue-160) 24%, #fff 24%); } + +/* find the role */ +.find-role { padding: 120px 0 130px; } +.find-role h2 { font-size: 90px; font-weight: 300; margin: 0 0 56px; letter-spacing: -.5px; } +.find-search { + display: flex; align-items: center; background: var(--ld-gray-5); border: 1px solid var(--ld-gray-20); + border-radius: 999px; padding: 14px 14px 14px 40px; +} +.find-search input { + flex: 1; min-width: 0; border: 0; outline: none; font-size: 24px; font-family: inherit; + font-weight: 300; color: var(--ld-blue-160); background: transparent; +} +.find-search input::placeholder { color: var(--ld-text-subtle); } +.find-search button { + border: 0; background: var(--ld-blue-100); color: #fff; border-radius: 999px; + width: 68px; height: 68px; display: grid; place-items: center; cursor: pointer; +} + +/* quote band */ +.quote-section { background: var(--ld-blue-100); color: #fff; padding: 160px 0; } +.quote-band-text { max-width: 620px; margin: 0 auto; font-size: 30px; font-weight: 300; line-height: 1.35; } + +.quote-cards { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; padding-top: 110px; } +.quote-card { + border-radius: 16px; background: #fff; box-shadow: 0 6px 24px rgba(0, 30, 96, .14); + padding: 24px 28px 30px; align-self: end; +} +.quote-card.on { transform: translateY(-40px); } +.quote-card b { display: block; font-size: 14px; margin-bottom: 10px; } +.quote-card p { margin: 0; font-size: 17px; line-height: 1.4; } + +.carousel { display: grid; grid-template-columns: repeat(5, 1fr); gap: 20px; } +.carousel a { + display: block; text-decoration: none; color: var(--ld-blue-160); + border-radius: 24px; overflow: hidden; background: var(--ld-blue-10); +} +.carousel img { display: block; width: 100%; height: 220px; object-fit: cover; } +.carousel .cap { padding: 16px 18px 20px; } +.carousel .cap b { display: block; font-size: 18px; } +.carousel .cap em { display: block; font-style: normal; color: var(--ld-text-subtle); font-size: 14px; } +.carousel .cap i { display: block; font-style: normal; margin-top: 8px; color: var(--ld-blue-100); font-weight: 600; font-size: 14px; } + +.stat-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; } +.stat-card { background: var(--ld-blue-10); border-radius: 24px; padding: 28px; } +.stat-card b { display: block; font-size: 34px; font-weight: 700; color: var(--ld-blue-100); } + +.tile-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; } +.tile-grid .tile { border-radius: 24px; overflow: hidden; background: var(--ld-gray-5); } +.tile-grid .tile img { display: block; width: 100%; height: 200px; object-fit: cover; } +.tile-grid .tile .cap { padding: 16px 18px; } + +/* ------------------------------ job cards ------------------------------- */ +.job-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 22px; } +.job-grid.one-col { grid-template-columns: 1fr; } +.job-grid.three-col { grid-template-columns: repeat(3, minmax(0, 1fr)); } +.job-card { + border: 1px solid var(--ld-gray-20); border-radius: 24px; padding: 22px 24px 24px; + background: #fff; display: block; position: relative; +} +.job-card:hover { border-color: var(--ld-blue-130); } +.job-card .spark { width: 24px; height: 24px; display: block; margin-bottom: 14px; } +.job-card h3 { margin: 0 0 10px; font-size: 17px; font-weight: 700; } +.job-card h3 a { color: var(--ld-blue-160); text-decoration: none; } +.job-card h3 a:hover { text-decoration: underline; } +.job-card .card-link::after { content: ""; position: absolute; inset: 0; border-radius: 24px; } +.job-card .meta { font-size: 15px; color: var(--ld-blue-160); } +.job-card .meta div { margin-bottom: 2px; } +.job-card .pay { margin-top: 6px; font-size: 15px; } +.job-card .actions { margin-top: 18px; display: flex; gap: 10px; align-items: center; position: relative; z-index: 1; } +.job-card .actions form { margin: 0; } +.job-card-compact { padding: 18px 22px 22px; } +.job-card-compact h3 { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } + +/* the live "Select +" pill */ +.pill { + display: inline-flex; align-items: center; gap: 10px; border-radius: 999px; + border: 1px solid var(--ld-blue-160); background: #fff; color: var(--ld-blue-160); + font: inherit; font-size: 14px; font-weight: 700; padding: 8px 18px; + text-decoration: none; cursor: pointer; +} +.pill span { font-weight: 400; font-size: 16px; } +.pill:hover { background: var(--ld-blue-9); color: var(--ld-blue-160); } +.pill-on { background: var(--ld-blue-160); color: #fff; } +.pill-on:hover { background: var(--ld-blue-130); color: #fff; } + +/* ------------------------------ results --------------------------------- */ +.results-layout { display: grid; grid-template-columns: 372px 1fr; gap: 28px; padding: 28px 0 64px; } +.results-layout { max-width: none; } +.results-page { padding: 0 24px; } +.results-aside .map-panel { border-radius: 20px; overflow: hidden; position: sticky; top: 100px; } +.cluster-map { display: block; } +.pin-card { display: block; border-radius: 16px; } + +.tabs { display: flex; gap: 32px; border-bottom: 1px solid var(--ld-gray-20); margin: 0 0 22px; } +.tabs a { + text-decoration: none; color: var(--ld-text-subtle); padding: 10px 2px 14px; + border-bottom: 3px solid transparent; font-weight: 600; display: inline-flex; + align-items: center; gap: 8px; +} +.tabs a.active { color: var(--ld-blue-160); border-bottom-color: var(--ld-blue-100); } +.tab-badge { + background: var(--ld-blue-100); color: #fff; border-radius: 999px; + padding: 2px 10px; font-size: 12px; font-weight: 700; +} + +.results-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 24px; } +.results-head h1 { font-size: 34px; font-weight: 300; margin: 0; } +.loc-note { color: var(--ld-text-subtle); font-size: 15px; margin: 10px 0 0; } + +/* popovers: Location, Filters and Sort by */ +.pop-wrap { position: relative; } +.pop-wrap > summary { list-style: none; cursor: pointer; } +.pop-wrap > summary::-webkit-details-marker { display: none; } +.pop-wrap > summary::marker { content: ""; } +.loc-link { + display: inline-flex; align-items: center; gap: 8px; color: var(--ld-blue-100); + font-size: 15px; padding: 8px 2px; +} +.loc-link:hover { text-decoration: underline; } +.tool-btn { + display: inline-flex; align-items: center; gap: 8px; color: var(--ld-blue-160); + font-size: 15px; padding: 8px 10px; border-radius: 8px; +} +.tool-btn:hover { background: var(--ld-gray-5); } +.tool-btn .caret { font-size: 12px; } +.toolbar { display: flex; justify-content: flex-end; align-items: center; gap: 18px; margin: 12px 0 16px; } +.active-filters { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin: 0 0 20px; } +.active-chip { + display: inline-flex; align-items: center; gap: 8px; border-radius: 999px; + border: 1px solid var(--ld-gray-20); background: var(--ld-blue-9); + color: var(--ld-blue-160); text-decoration: none; padding: 6px 14px; font-size: 14px; +} +.active-chip:hover { border-color: var(--ld-blue-130); color: var(--ld-blue-160); } +.clear-all { font-size: 14px; color: var(--ld-blue-160); text-decoration: underline; } + +.pop-panel { + position: absolute; right: 0; top: calc(100% + 8px); z-index: 40; background: #fff; + border-radius: 20px; box-shadow: 0 10px 34px rgba(0, 30, 96, .22); padding: 22px 24px; + min-width: 340px; text-align: left; +} +.pop-panel.wide { min-width: 700px; } +.pop-head h2 { font-size: 20px; font-weight: 400; margin: 0 0 16px; } +.pop-panel .field { margin-bottom: 14px; } +.pop-panel .radio-row { + display: flex; align-items: center; gap: 10px; padding: 7px 0; font-size: 15px; + color: var(--ld-blue-160); text-decoration: none; cursor: pointer; +} +.pop-panel .radio-row:hover { color: var(--ld-blue-100); } +.pop-actions { + display: flex; justify-content: flex-end; align-items: center; gap: 18px; + border-top: 1px solid var(--ld-gray-20); margin-top: 16px; padding-top: 16px; +} +.pop-actions .reset { color: var(--ld-blue-160); text-decoration: underline; font-size: 15px; } + +.filter-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 22px; } +.filter-grid fieldset { border: 0; padding: 0; margin: 0; min-width: 0; } +.filter-grid legend { font-weight: 700; padding: 0 0 8px; font-size: 16px; } +.filter-grid label { display: flex; gap: 8px; align-items: flex-start; font-size: 14px; padding: 4px 0; cursor: pointer; } +.filter-grid .cat-group { margin: 2px 0 10px 20px; } +.filter-grid .cat-group summary { cursor: pointer; font-size: 13px; padding: 3px 0; color: var(--ld-text-subtle); } +.area-col { max-height: 420px; overflow-y: auto; } + +.empty-panel { background: var(--ld-blue-9); border-radius: 24px; padding: 32px; } + +.pagination { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 32px; align-items: center; justify-content: center; } +.pagination a, .pagination span { + min-width: 40px; height: 40px; border-radius: 999px; display: grid; place-items: center; + text-decoration: none; border: 1px solid transparent; color: var(--ld-blue-160); padding: 0 12px; +} +.pagination a:hover { background: var(--ld-gray-5); } +.pagination .current { border-color: var(--ld-blue-100); color: var(--ld-blue-100); font-weight: 700; } + +/* ------------------------------ job detail ------------------------------ */ +.detail-top { padding: 22px 0 0; } +.hero-grid { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 24px; align-items: start; } +.hero-col { display: flex; flex-direction: column; gap: 24px; } +.spark-pill { + background: var(--ld-blue-100); border-radius: 999px; height: 74px; + display: grid; place-items: center; +} +.spark-pill img { height: 34px; width: auto; } +.hero-photo-tall { display: block; width: 100%; height: 424px; object-fit: cover; border-radius: 20px; } +.hero-photo-wide { display: block; width: 100%; height: 298px; object-fit: cover; border-radius: 20px; } + +.id-card { border-radius: 20px; overflow: hidden; color: #fff; } +.id-card .id-top { background: var(--ld-blue-100); padding: 14px 0 10px; text-align: center; } +.id-card .grip { + display: block; width: 42px; height: 5px; border-radius: 999px; + background: rgba(255, 255, 255, .9); margin: 0 auto 14px; +} +.id-card .id-spark { height: 30px; width: auto; } +.id-card .id-body { padding: 12px 20px 18px; text-align: center; } +.id-card .id-title { font-size: 18px; } +.id-card .id-loc { font-size: 14px; font-weight: 700; margin-top: 8px; } +.id-salaried .id-body { background: var(--ld-blue-100); } +.id-hourly .id-body { background: var(--ld-blue-160); } +.id-card .id-actions { + background: #fff; display: flex; align-items: center; gap: 8px; padding: 12px 16px; + border: 1px solid var(--ld-gray-20); border-top: 0; + border-radius: 0 0 20px 20px; +} +.id-card .id-actions form { margin: 0; } +.icon-btn { + background: none; border: 0; cursor: pointer; color: var(--ld-blue-160); + width: 36px; height: 36px; border-radius: 999px; display: grid; place-items: center; padding: 0; +} +.icon-btn:hover { background: var(--ld-gray-200); color: var(--ld-blue-160); } +.apply-now { margin-left: auto; display: inline-flex; align-items: center; gap: 8px; } +.apply-now span { font-weight: 400; } + +.detail-layout { + display: grid; grid-template-columns: 236px minmax(0, 1000px); gap: 104px; + padding: 40px 100px 0 0; max-width: 1440px; margin: 0 auto; +} +.detail-nav { + position: sticky; top: 100px; align-self: start; background: var(--ld-blue-9); + border-radius: 0 24px 24px 0; padding: 26px 20px 26px 24px; +} +.detail-nav ul { list-style: none; margin: 0; padding: 0; } +.detail-nav li { padding: 7px 0; font-size: 14px; } +.detail-nav a { text-decoration: none; color: var(--ld-blue-160); } +.detail-nav li:first-child a { font-weight: 700; } +.detail-nav .sub { padding-left: 16px; font-size: 13px; } + +.detail-main h1 { font-size: 44px; font-weight: 300; margin: 0 0 30px; line-height: 1.1; } +.fact-row { display: grid; grid-template-columns: 1fr 490px; gap: 32px; align-items: start; } +.fact-col .banner-line { font-weight: 700; font-size: 20px; margin-bottom: 4px; } +.fact-col address { font-style: normal; line-height: 1.55; margin: 0 0 16px; } +.positions { + display: inline-block; background: var(--ld-blue-9); border-radius: 6px; + padding: 4px 10px; font-size: 13px; color: var(--ld-blue-160); +} +.fact-col .req-id { display: inline-block; font-size: 14px; color: var(--ld-blue-100); margin-top: 12px; text-decoration: none; } +.fact-col .req-id:hover { text-decoration: underline; } +.map-col { border-radius: 16px; overflow: hidden; } + +.chips { display: flex; flex-wrap: wrap; gap: 24px; margin: 28px 0 8px; } +.chip { + background: var(--ld-blue-160); color: #fff; border-radius: 24px; + padding: 20px 26px 18px; font-size: 15px; display: inline-flex; flex-direction: column; + align-items: flex-start; gap: 12px; min-height: 100px; min-width: 232px; justify-content: center; +} +.chip svg { color: #fff; width: 28px; height: 28px; } +.footnote { font-size: 13px; color: var(--ld-blue-160); margin-top: 14px; } + +.detail-body { margin-top: 36px; } +.detail-body h2 { font-size: 30px; font-weight: 300; margin: 36px 0 14px; } +.detail-body h2:first-child { margin-top: 0; } +.detail-body p { margin: 0 0 14px; } +.detail-body ul { margin: 0 0 16px; padding-left: 20px; } +.detail-body li { margin-bottom: 8px; } +a.hashtag { font-weight: 400; color: var(--ld-blue-100); text-decoration: none; } +a.hashtag:hover { text-decoration: underline; } +.detail-body .sub-heading { font-weight: 700; margin: 22px 0 6px; } +/* Workday-style salaried postings: bold run-in headings instead of the hourly h2s */ +.detail-body.salaried h2 { font-size: 15px; font-weight: 700; margin: 18px 0 4px; line-height: 1.4; } +.detail-body.salaried .preamble { font-style: italic; } +.legal { font-size: 13px; color: var(--ld-text-subtle); margin-top: 20px; } + +/* related roles under the detail layout */ +.related-section { padding: 110px 0 90px; } +.related-section .wrap-hub { max-width: 1440px; margin: 0 auto; padding: 0 120px; } +.related-section h2 { font-size: 22px; font-weight: 400; margin: 0 0 20px; } + +.benefit-tiles { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; } +.benefit-tile { background: var(--ld-blue-160); color: #fff; border-radius: 24px; padding: 26px; } +.benefit-tile img { width: 44px; height: 44px; margin-bottom: 12px; } +.benefit-tile b { display: block; font-size: 20px; } +.benefit-tile em { font-style: normal; display: block; color: var(--ld-spark-100); margin-bottom: 10px; } +.benefit-tile p { font-size: 13px; margin: 8px 0 0; line-height: 1.45; } +.benefit-tile.tile-1 { background: var(--ld-blue-100); } +.benefit-tile.tile-1 em { color: #fff; font-weight: 700; } +.benefit-tile.tile-2 { background: var(--ld-sky); color: var(--ld-blue-160); } +.benefit-tile.tile-2 em { color: var(--ld-blue-160); font-weight: 700; } +.benefit-tile.tile-2 img { filter: brightness(0) saturate(100%) invert(9%) sepia(60%) saturate(4000%) hue-rotate(215deg); } +.benefit-tile.tile-3 em { color: #fff; font-weight: 700; } + +/* Life at Walmart (shared partial) */ +.life-block { padding: 90px 0 0; } +.life-block h2 { font-size: 40px; font-weight: 300; margin: 0 0 30px; line-height: 1.15; } +.life-lead { font-size: 24px; line-height: 1.35; margin: 0 0 44px; max-width: 960px; } +.life-grid { display: grid; grid-template-columns: minmax(0, 1fr) 486px; gap: 34px; align-items: start; } +.life-grid p { font-size: 14px; margin: 0 0 14px; } +.life-media img { display: block; width: 100%; height: 328px; object-fit: cover; border-radius: 20px; margin-bottom: 24px; } +.life-band { + background: var(--ld-blue-100); color: #fff; margin: 40px -100px 40px -100px; padding: 100px 120px; +} +.life-band p { max-width: 600px; margin: 0 auto; font-size: 30px; font-weight: 300; line-height: 1.35; } +.life-closing { font-size: 20px; font-weight: 300; color: #3f5a8f; margin: 0 0 14px; line-height: 1.4; } +.life-note { font-size: 15px; margin: 0; } +.about-life .life-band { margin-left: 0; margin-right: 0; border-radius: 24px; } + +/* ------------------------------ forms ----------------------------------- */ +.form-card { max-width: 520px; margin: 48px auto; border: 1px solid var(--ld-gray-20); border-radius: 24px; padding: 32px; } +.form-wide { max-width: 720px; } +.form-card h1 { font-size: 28px; font-weight: 400; margin: 0 0 20px; } +.field { margin-bottom: 16px; } +.field label { display: block; font-weight: 600; margin-bottom: 6px; font-size: 15px; } +.field input[type=text], .field input[type=email], .field input[type=password], .field input[type=tel], .field select { + width: 100%; padding: 12px 14px; border: 1px solid var(--ld-gray-20); border-radius: 12px; + font: inherit; background: var(--ld-gray-5); color: var(--ld-blue-160); +} +.field .check { display: flex; gap: 10px; align-items: flex-start; font-weight: 400; } +.errors { background: #fdecec; border: 1px solid var(--ld-red); color: var(--ld-red); border-radius: 12px; padding: 14px 18px; margin-bottom: 18px; } +.errors ul { margin: 0; padding-left: 18px; } +.flash { border-radius: 12px; padding: 12px 18px; margin: 16px 0; } +.flash.success { background: #eaf6e6; border: 1px solid var(--ld-green); color: #1c5c02; } +.flash.info { background: var(--ld-blue-9); border: 1px solid var(--ld-blue-100); } +.flash.warning { background: #fff5e0; border: 1px solid var(--ld-spark-100); color: #7a5a00; } +.form-note { font-size: 14px; color: var(--ld-text-subtle); margin-top: 14px; } + +/* stripped sign-in / register layout (identity.walmart.com) */ +.auth-body { background: #fff; min-height: 100vh; display: flex; flex-direction: column; } +.auth-main { flex: 1 0 auto; max-width: 352px; margin: 0 auto; padding: 28px 0 60px; width: 100%; } +.auth-spark { display: block; width: 56px; margin: 0 auto 20px; } +.auth-spark img { display: block; width: 56px; height: 56px; } +.auth-card h1 { font-size: 20px; font-weight: 700; text-align: center; margin: 0 0 22px; } +.auth-lead { text-align: center; font-size: 17px; margin: 0 0 26px; line-height: 1.4; } +.auth-card .field label { font-size: 14px; font-weight: 700; } +.auth-card .field input { + background: #fff; border: 1px solid var(--ld-blue-160); border-radius: 8px; padding: 16px 14px; +} +.auth-card .form-note { text-align: center; } +.auth-flash { margin: 0 0 18px; } +.auth-footer { + border-top: 1px solid var(--ld-gray-20); padding: 24px 80px 40px; display: flex; gap: 24px; + align-items: center; justify-content: space-between; font-size: 13px; color: var(--ld-blue-160); +} +.auth-footer nav { display: flex; gap: 24px; flex-wrap: wrap; } +.auth-footer a { color: var(--ld-blue-160); text-decoration: none; font-size: 14px; } +.auth-footer a:hover { text-decoration: underline; } + +.apply-page { background: #f0f8fa; padding-bottom: 140px; } +.apply-band { background: var(--ld-blue-100); height: 140px; } +.apply-card { + background: #fff; border-radius: 24px; max-width: 920px; margin: -80px auto 0; + padding: 60px 120px 48px; box-shadow: 0 1px 3px rgba(0, 30, 96, .08); +} +.apply-card h1 { font-size: 26px; font-weight: 300; margin: 0 0 22px; } +.apply-sub { font-size: 14px; margin: 0 0 24px; } +.apply-card .field label { font-size: 13px; font-weight: 700; } +.apply-card .field input[type=text], .apply-card .field input[type=email], .apply-card .field input[type=tel] { + background: #fff; border: 1px solid var(--ld-blue-160); border-radius: 4px; padding: 11px 12px; +} +.apply-hint { font-size: 11px; color: var(--ld-gray-100); margin: 6px 0 0; } +.field-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; } +.apply-card .check { font-size: 13px; margin-top: 8px; } +.apply-card .check input { width: 18px; height: 18px; margin: 0; } +.apply-actions { display: flex; justify-content: flex-end; align-items: center; gap: 28px; margin-top: 32px; } +.apply-actions-start { justify-content: flex-start; gap: 12px; } +.apply-back { font-size: 14px; color: var(--ld-blue-160); } +.review-list { list-style: none; margin: 0 0 22px; padding: 0; } +.review-list li { display: flex; justify-content: space-between; gap: 20px; padding: 10px 0; border-bottom: 1px solid var(--ld-gray-20); } +.review-list b { font-weight: 600; } +.confirmation { + background: var(--ld-blue-10); border-radius: 24px; padding: 28px; margin: 24px 0; + font-size: 22px; font-weight: 700; +} + +table.data { width: 100%; border-collapse: collapse; } +table.data th, table.data td { text-align: left; padding: 12px 10px; border-bottom: 1px solid var(--ld-gray-20); } +table.data th { font-size: 14px; text-transform: uppercase; letter-spacing: .04em; color: var(--ld-text-subtle); } + +/* ------------------------------ area / locations ------------------------ */ +.area-hero { position: relative; } +.area-hero img { width: 100%; height: 780px; object-fit: cover; display: block; } +.area-card { + position: absolute; left: var(--gutter); top: 250px; width: 700px; background: var(--ld-blue-10); + border-radius: 24px; padding: 40px 30px 30px 16px; color: var(--ld-blue-160); +} +.area-card h1 { font-size: 34px; font-weight: 300; margin: 0 0 24px; } +.area-card p { font-size: 18px; line-height: 1.45; margin: 0 0 24px; } + +.join-grid { display: grid; grid-template-columns: 696px 1fr; gap: 116px; align-items: start; } +.stack-card { position: relative; padding-top: 40px; } +.stack-band { display: block; height: 30px; border-radius: 24px 24px 0 0; } +.band-navy { background: var(--ld-blue-130); position: absolute; top: 0; left: 0; right: 0; height: 60px; } +.band-sky { background: var(--ld-blue-10); position: absolute; top: 36px; left: 0; right: 0; height: 60px; } +.stack-card img { display: block; width: 100%; height: 450px; object-fit: cover; border-radius: 24px; position: relative; margin-top: 36px; } +.stack-controls { + position: absolute; left: 0; right: 0; bottom: 60px; display: flex; justify-content: center; gap: 22px; +} +.stack-controls i { width: 22px; height: 22px; border-radius: 999px; border: 1px solid #fff; color: #fff; font-style: normal; font-size: 12px; display: grid; place-items: center; } +.category-list { list-style: none; margin: 0; padding: 0; } +.category-list li { margin: 0 0 26px; } +.category-list a { font-size: 24px; font-weight: 300; text-decoration: none; color: var(--ld-blue-160); line-height: 1.3; } +.category-list a:hover { text-decoration: underline; color: var(--ld-blue-100); } +.category-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; } + +.hub-links { display: flex; justify-content: space-between; gap: 24px; padding: 60px 0 0; } +.hub-links a { + display: inline-flex; align-items: center; gap: 16px; text-decoration: none; color: var(--ld-blue-160); + font-size: 18px; font-weight: 300; +} +.hub-links a:hover { color: var(--ld-blue-100); } + +.hub-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 24px; } +.hub-card { border: 1px solid var(--ld-gray-20); border-radius: 24px; overflow: hidden; } +.hub-card img { display: block; width: 100%; height: 220px; object-fit: cover; } +.hub-card .cap { padding: 22px; } + +.loc-hero { position: relative; border-radius: 24px; overflow: hidden; margin-top: 0; } +.loc-hero img { display: block; width: 100%; height: 480px; object-fit: cover; } +.loc-hero h1 { + position: absolute; left: 60px; bottom: 60px; margin: 0; color: #fff; font-size: 72px; font-weight: 300; + text-shadow: 0 2px 12px rgba(0, 0, 0, .35); +} +.loc-hero .pause { + position: absolute; right: 40px; top: 40px; width: 48px; height: 48px; border-radius: 999px; + border: 1px solid #fff; color: #fff; display: grid; place-items: center; font-size: 16px; +} +.loc-intro { padding: 54px 0 20px; } +.loc-intro p { font-size: 22px; font-weight: 300; max-width: 720px; margin: 0; line-height: 1.35; } +.hub-tiles { display: grid; grid-template-columns: repeat(3, 1fr); gap: 34px 28px; } +.hub-tile img { display: block; width: 100%; height: 202px; object-fit: cover; border-radius: 16px; margin-bottom: 14px; } +.hub-tile b { display: block; font-size: 14px; margin-bottom: 8px; } +.hub-tile p { font-size: 13px; margin: 0 0 24px; line-height: 1.45; } +.arrow-link { display: inline-flex; color: var(--ld-blue-160); } +.arrow-link:hover { color: var(--ld-blue-100); } + +/* saved roles promo */ +.promo-section { padding: 80px 0 60px; } +.promo { display: grid; grid-template-columns: 1fr 565px; gap: 40px; align-items: start; } +.promo h1 { font-size: 64px; font-weight: 300; line-height: 1.12; margin: 0 0 20px; } +.promo p { font-size: 17px; max-width: 560px; margin: 0 0 32px; } +.promo-photo { display: block; width: 565px; height: 376px; object-fit: cover; border-radius: 24px; } +.saved-section { padding: 40px 0 20px; } +.saved-heading { font-size: 48px; font-weight: 300; margin: 0 0 28px; } +.saved-note { font-size: 18px; max-width: 420px; margin: 0 0 40px; line-height: 1.4; } +.trending-section { padding: 20px 0 60px; } +.trending-heading { font-size: 22px; font-weight: 400; margin: 0 0 20px; } + +.faq-section { padding: 40px 0; } +.faq-layout { display: grid; grid-template-columns: 340px minmax(0, 1fr); gap: 150px; align-items: start; } +.faq-intro img { width: 150px; height: 150px; display: block; margin-bottom: 10px; } +.faq-intro h2 { font-size: 40px; font-weight: 300; margin: 0 0 14px; } +.faq-intro p { font-size: 14px; margin: 0; } +.faq details { + border: 1px solid var(--ld-gray-20); border-radius: 16px; padding: 0; margin-bottom: 18px; + background: #fff; +} +.faq summary { + cursor: pointer; font-weight: 700; font-size: 14px; padding: 22px 60px 22px 24px; list-style: none; + position: relative; +} +.faq summary::-webkit-details-marker { display: none; } +.faq summary::after { + content: ""; position: absolute; right: 26px; top: 22px; width: 10px; height: 10px; + border-right: 2px solid var(--ld-blue-160); border-bottom: 2px solid var(--ld-blue-160); + transform: rotate(45deg); +} +.faq details[open] summary::after { transform: rotate(225deg); top: 28px; } +.faq p { margin: 0; padding: 0 24px 22px; font-size: 14px; } + +.feature-section { padding: 90px 0 40px; } +.feature-row { display: grid; grid-template-columns: 690px minmax(0, 1fr); gap: 24px; align-items: center; margin-bottom: 70px; } +.feature-row img { display: block; width: 100%; height: 460px; object-fit: cover; border-radius: 24px; } +.feature-text h3 { font-size: 30px; font-weight: 400; margin: 0 0 14px; line-height: 1.25; } +.feature-text p { font-size: 14px; margin: 0 0 20px; } +.program-tiles { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; margin-top: 30px; } +.program-tile { display: block; text-decoration: none; color: var(--ld-blue-160); } +.program-tile img { display: block; width: 100%; height: 320px; object-fit: cover; border-radius: 24px; margin-bottom: 18px; } +.program-tile b { display: block; font-size: 14px; margin-bottom: 8px; } +.program-tile p { font-size: 13px; margin: 0 0 22px; line-height: 1.45; } +.program-tile:hover { color: var(--ld-blue-100); } + +.meet-section { padding: 30px 0 60px; } +.meet-layout { display: grid; grid-template-columns: 760px minmax(0, 1fr); gap: 24px; align-items: start; } +.meet-tile { height: 430px; } +.meet-tile .v-spark { width: 110px; height: 110px; top: 130px; } +.meet-text h2 { font-size: 40px; font-weight: 300; margin: 20px 0 30px; } +.meet-text p { font-size: 14px; margin: 0; } +.values-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px; } +.seg-6 { background: var(--ld-sky-60); color: var(--ld-blue-160); z-index: 0; } +.steps { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; } +.steps .step { background: var(--ld-blue-10); border-radius: 24px; padding: 24px; } + +/* ------------------------------ footer ---------------------------------- */ +.site-footer { background: var(--ld-blue-160); color: #fff; padding: 80px 0 60px; margin-top: 0; } +.site-footer .wrap { padding: 0 9.2%; max-width: none; } +.site-footer a { color: #fff; text-decoration: none; } +.site-footer a:hover { text-decoration: underline; color: #fff; } +.footer-cols { display: grid; grid-template-columns: repeat(4, 240px); gap: 0; margin-bottom: 44px; } +.footer-cols h4 { font-size: 24px; font-weight: 400; margin: 0 0 22px; } +.footer-cols ul { list-style: none; margin: 0; padding: 0; } +.footer-cols li { margin-bottom: 22px; font-size: 14px; padding-right: 24px; } +/* the harvested glyphs are dark (#151F29): the live footer draws them on white discs */ +.social { display: flex; gap: 12px; margin-bottom: 44px; } +.social-btn { + width: 44px; height: 44px; border-radius: 999px; background: #fff; + display: grid; place-items: center; flex: 0 0 auto; +} +.social-btn img { width: 24px; height: 25px; display: block; } +.legal-text { font-size: 11px; line-height: 2.3; color: #fff; margin: 0 0 22px; max-width: 1180px; } +.legal-link { font-weight: 700; text-decoration: underline !important; } +.footer-bottom { display: flex; gap: 12px; flex-wrap: wrap; font-size: 12px; padding-top: 8px; align-items: center; } +.footer-bottom a { text-decoration: underline; } + +/* Header: below 1024px the five nav items plus the search pill no longer fit on one + row (the header used to force the viewport wider than 768px), so the header wraps + and the search pill takes a full second row. */ +@media (max-width: 1024px) { + .site-header .wrap { flex-wrap: wrap; gap: 8px 12px; padding: 8px 16px; } + .main-nav { flex: 1 1 auto; gap: 2px; min-width: 0; overflow-x: auto; } + .main-nav .nav-link, .nav-menu > summary { font-size: 14px; padding: 8px 8px; } + .header-search { flex: 1 0 100%; order: 10; } + .header-search form { max-width: none; } + .header-spacer { display: none; } +} + +@media (max-width: 1200px) { + :root { --gutter: 32px; } + .results-layout, .detail-layout { grid-template-columns: 1fr; } + .detail-layout { padding: 32px 24px 0; gap: 32px; } + .detail-nav { border-radius: 24px; } + .life-grid, .faq-layout, .feature-row, .meet-layout, .program-tiles, .benefit-rows, .field-pair, .values-grid { grid-template-columns: 1fr; } + .life-band { margin-left: 0; margin-right: 0; border-radius: 24px; padding: 60px 32px; } + .footer-cols { grid-template-columns: repeat(2, 1fr); gap: 24px; } + .apply-card { margin-left: 24px; margin-right: 24px; padding: 40px 32px; } + .related-section .wrap-hub { padding: 0 24px; } + .faq-layout { gap: 40px; } + .feature-row img, .meet-tile { height: 320px; } + .carousel, .stat-grid, .tile-grid, .steps, .footer-cols { grid-template-columns: repeat(2, 1fr); } + .job-grid, .job-grid.three-col, .category-grid, .benefit-tiles, .hub-grid, .hub-tiles, + .promo, .join-grid, .bento-layout, .benefits-layout, .milestone-layout { grid-template-columns: 1fr; } + .bento { grid-template-columns: repeat(2, 1fr); } + .tile-sq, .tile-wide, .tile-short, .tile-mini, .tile-gap { grid-column: span 1; } + .hero-strip { position: static; grid-template-columns: 1fr; padding: 0; } + .hero .inner { padding: 56px 32px; } + .after-hero { padding-top: 56px; } + .hero-grid { grid-template-columns: 1fr; } + .fact-row, .chips { grid-template-columns: 1fr; } + .filter-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } + .pop-panel, .pop-panel.wide { min-width: 300px; } + .detail-nav, .fact-card, .results-aside .map-panel { position: static; } + .hero h1, .find-role h2 { font-size: 40px; } + .area-card { position: static; width: auto; margin: -80px 32px 0; } + .area-hero img { height: 480px; } + .video-strip { grid-template-columns: 1fr; } + .badge, .promo-photo { width: 100%; } + .milestone-layout h2 { margin-top: 0; } + .ribbon { flex-direction: column; } + .ribbon-seg, .ribbon-seg:first-child { margin-left: 0; padding-left: 24px; border-radius: 0; } +} diff --git a/sites/walmart_careers/static/fonts/EverydaySansUI-wght.ttf b/sites/walmart_careers/static/fonts/EverydaySansUI-wght.ttf new file mode 100644 index 00000000..7b258008 Binary files /dev/null and b/sites/walmart_careers/static/fonts/EverydaySansUI-wght.ttf differ diff --git a/sites/walmart_careers/static/fonts/LivingDesign.woff b/sites/walmart_careers/static/fonts/LivingDesign.woff new file mode 100644 index 00000000..f6ca2a57 Binary files /dev/null and b/sites/walmart_careers/static/fonts/LivingDesign.woff differ diff --git a/sites/walmart_careers/static/icons/.gitkeep b/sites/walmart_careers/static/icons/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/sites/walmart_careers/static/icons/benefit-financial.svg b/sites/walmart_careers/static/icons/benefit-financial.svg new file mode 100644 index 00000000..ca73078f --- /dev/null +++ b/sites/walmart_careers/static/icons/benefit-financial.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sites/walmart_careers/static/icons/benefit-growth.svg b/sites/walmart_careers/static/icons/benefit-growth.svg new file mode 100644 index 00000000..69f7fdc2 --- /dev/null +++ b/sites/walmart_careers/static/icons/benefit-growth.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/sites/walmart_careers/static/icons/benefit-health.svg b/sites/walmart_careers/static/icons/benefit-health.svg new file mode 100644 index 00000000..ccccbd9a --- /dev/null +++ b/sites/walmart_careers/static/icons/benefit-health.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sites/walmart_careers/static/icons/benefit-pto.svg b/sites/walmart_careers/static/icons/benefit-pto.svg new file mode 100644 index 00000000..5df7085e --- /dev/null +++ b/sites/walmart_careers/static/icons/benefit-pto.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/sites/walmart_careers/static/icons/benefit-wellbeing.svg b/sites/walmart_careers/static/icons/benefit-wellbeing.svg new file mode 100644 index 00000000..f4982f36 --- /dev/null +++ b/sites/walmart_careers/static/icons/benefit-wellbeing.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sites/walmart_careers/static/icons/expand.svg b/sites/walmart_careers/static/icons/expand.svg new file mode 100644 index 00000000..3ccbb1bc --- /dev/null +++ b/sites/walmart_careers/static/icons/expand.svg @@ -0,0 +1,3 @@ + + + diff --git a/sites/walmart_careers/static/icons/header-mobile-logo.svg b/sites/walmart_careers/static/icons/header-mobile-logo.svg new file mode 100644 index 00000000..47f18701 --- /dev/null +++ b/sites/walmart_careers/static/icons/header-mobile-logo.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/sites/walmart_careers/static/icons/heart-blue.svg b/sites/walmart_careers/static/icons/heart-blue.svg new file mode 100644 index 00000000..f15b2474 --- /dev/null +++ b/sites/walmart_careers/static/icons/heart-blue.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/sites/walmart_careers/static/icons/home-logo.svg b/sites/walmart_careers/static/icons/home-logo.svg new file mode 100644 index 00000000..a080f4fc --- /dev/null +++ b/sites/walmart_careers/static/icons/home-logo.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/sites/walmart_careers/static/icons/sams-club-text.svg b/sites/walmart_careers/static/icons/sams-club-text.svg new file mode 100644 index 00000000..a96751ae --- /dev/null +++ b/sites/walmart_careers/static/icons/sams-club-text.svg @@ -0,0 +1,4 @@ + + + diff --git a/sites/walmart_careers/static/icons/sams-logo.svg b/sites/walmart_careers/static/icons/sams-logo.svg new file mode 100644 index 00000000..a36fca73 --- /dev/null +++ b/sites/walmart_careers/static/icons/sams-logo.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/sites/walmart_careers/static/icons/sams-spark.svg b/sites/walmart_careers/static/icons/sams-spark.svg new file mode 100644 index 00000000..0f3656c0 --- /dev/null +++ b/sites/walmart_careers/static/icons/sams-spark.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/sites/walmart_careers/static/icons/search_icon.png b/sites/walmart_careers/static/icons/search_icon.png new file mode 100644 index 00000000..b8cc7802 Binary files /dev/null and b/sites/walmart_careers/static/icons/search_icon.png differ diff --git a/sites/walmart_careers/static/icons/social-facebook.svg b/sites/walmart_careers/static/icons/social-facebook.svg new file mode 100644 index 00000000..406cd57c --- /dev/null +++ b/sites/walmart_careers/static/icons/social-facebook.svg @@ -0,0 +1,3 @@ + + + diff --git a/sites/walmart_careers/static/icons/social-glassdoor.svg b/sites/walmart_careers/static/icons/social-glassdoor.svg new file mode 100644 index 00000000..f16bd4c7 --- /dev/null +++ b/sites/walmart_careers/static/icons/social-glassdoor.svg @@ -0,0 +1,3 @@ + + + diff --git a/sites/walmart_careers/static/icons/social-instagram.svg b/sites/walmart_careers/static/icons/social-instagram.svg new file mode 100644 index 00000000..0f11f5c9 --- /dev/null +++ b/sites/walmart_careers/static/icons/social-instagram.svg @@ -0,0 +1,4 @@ + + + + diff --git a/sites/walmart_careers/static/icons/social-linkedin.svg b/sites/walmart_careers/static/icons/social-linkedin.svg new file mode 100644 index 00000000..be19b763 --- /dev/null +++ b/sites/walmart_careers/static/icons/social-linkedin.svg @@ -0,0 +1,3 @@ + + + diff --git a/sites/walmart_careers/static/icons/social-x.svg b/sites/walmart_careers/static/icons/social-x.svg new file mode 100644 index 00000000..7ab8ac69 --- /dev/null +++ b/sites/walmart_careers/static/icons/social-x.svg @@ -0,0 +1,3 @@ + + + diff --git a/sites/walmart_careers/static/icons/social-youtube.svg b/sites/walmart_careers/static/icons/social-youtube.svg new file mode 100644 index 00000000..cffa63ed --- /dev/null +++ b/sites/walmart_careers/static/icons/social-youtube.svg @@ -0,0 +1,3 @@ + + + diff --git a/sites/walmart_careers/static/icons/spark-white.svg b/sites/walmart_careers/static/icons/spark-white.svg new file mode 100644 index 00000000..a757601d --- /dev/null +++ b/sites/walmart_careers/static/icons/spark-white.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/sites/walmart_careers/static/icons/spark-yellow-card.svg b/sites/walmart_careers/static/icons/spark-yellow-card.svg new file mode 100644 index 00000000..4ac91fd6 --- /dev/null +++ b/sites/walmart_careers/static/icons/spark-yellow-card.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/sites/walmart_careers/static/icons/spark-yellow.svg b/sites/walmart_careers/static/icons/spark-yellow.svg new file mode 100644 index 00000000..8cfd8cce --- /dev/null +++ b/sites/walmart_careers/static/icons/spark-yellow.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/sites/walmart_careers/static/icons/spark.svg b/sites/walmart_careers/static/icons/spark.svg new file mode 100644 index 00000000..8a1f4a3e --- /dev/null +++ b/sites/walmart_careers/static/icons/spark.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/sites/walmart_careers/static/icons/tile-card.svg b/sites/walmart_careers/static/icons/tile-card.svg new file mode 100644 index 00000000..fff61e75 --- /dev/null +++ b/sites/walmart_careers/static/icons/tile-card.svg @@ -0,0 +1,3 @@ + + + diff --git a/sites/walmart_careers/static/icons/tile-graduation.svg b/sites/walmart_careers/static/icons/tile-graduation.svg new file mode 100644 index 00000000..aaa269a4 --- /dev/null +++ b/sites/walmart_careers/static/icons/tile-graduation.svg @@ -0,0 +1,3 @@ + + + diff --git a/sites/walmart_careers/static/icons/tile-growth.svg b/sites/walmart_careers/static/icons/tile-growth.svg new file mode 100644 index 00000000..cdb231e1 --- /dev/null +++ b/sites/walmart_careers/static/icons/tile-growth.svg @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/sites/walmart_careers/static/icons/tile-walmart-plus.svg b/sites/walmart_careers/static/icons/tile-walmart-plus.svg new file mode 100644 index 00000000..8968fb4a --- /dev/null +++ b/sites/walmart_careers/static/icons/tile-walmart-plus.svg @@ -0,0 +1,3 @@ + + + diff --git a/sites/walmart_careers/static/icons/walmart-logo.svg b/sites/walmart_careers/static/icons/walmart-logo.svg new file mode 100644 index 00000000..57324ddd --- /dev/null +++ b/sites/walmart_careers/static/icons/walmart-logo.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/sites/walmart_careers/static/js/.gitkeep b/sites/walmart_careers/static/js/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/sites/walmart_careers/tasks.jsonl b/sites/walmart_careers/tasks.jsonl new file mode 100644 index 00000000..cbb7cf91 --- /dev/null +++ b/sites/walmart_careers/tasks.jsonl @@ -0,0 +1,20 @@ +{"web_name": "Walmart Careers", "id": "Walmart Careers--0", "ques": "Search for Optician roles and open the posting at the Neighborhood Market in Wichita, KS. Report its requisition ID and the street address shown on the posting.", "web": "http://localhost:40019/", "upstream_url": "https://careers.walmart.com/us/en/results?searchQuery=optician"} +{"web_name": "Walmart Careers", "id": "Walmart Careers--1", "ques": "Find the Staff, Software Engineer posting located in Sunnyvale, CA and quote the exact text of \"Option 2\" under Minimum Qualifications.", "web": "http://localhost:40019/", "upstream_url": "https://careers.walmart.com/us/en/jobs/R-2463275"} +{"web_name": "Walmart Careers", "id": "Walmart Careers--2", "ques": "Open the Freight Handler posting at eComm Whse Logistics #9054 in Porterville, CA. What shift start window does it list, and how many open positions does it show?", "web": "http://localhost:40019/", "upstream_url": "https://careers.walmart.com/us/en/jobs/CP-9054-11013"} +{"web_name": "Walmart Careers", "id": "Walmart Careers--3", "ques": "From the Healthcare career area page, go to its open roles and open the Pharmacy Technician posting in Bentonville, AR. What hashtag appears at the end of the \"What you'll bring\" section, and how many open positions does the posting list?", "web": "http://localhost:40019/", "upstream_url": "https://careers.walmart.com/us/en/home/careers-areas/healthcare"} +{"web_name": "Walmart Careers", "id": "Walmart Careers--4", "ques": "Using the filters, find Sam's Club Part time roles with a Weekend Overnight shift whose posted pay range tops out at $20.00/hr or less. Open the one in Texas and report its requisition ID and number of open positions.", "web": "http://localhost:40019/", "upstream_url": "https://careers.walmart.com/us/en/results?searchQuery=All"} +{"web_name": "Walmart Careers", "id": "Walmart Careers--5", "ques": "Find Full time Technology roles in Hoboken, NJ whose salary range tops out above $200,000. Open the matching posting and report its requisition ID and the degree named in \"Option 1\" of its Minimum Qualifications.", "web": "http://localhost:40019/", "upstream_url": "https://careers.walmart.com/us/en/results?searchQuery=All&careerareas=Technology"} +{"web_name": "Walmart Careers", "id": "Walmart Careers--6", "ques": "Set your location to Cleveland, OH within 25 miles, filter to Full time roles on a Weekday Day shift, and open the Online Order Filling Team Supervisor posting. Report the street address and the number of open positions.", "web": "http://localhost:40019/", "upstream_url": "https://careers.walmart.com/us/en/results?searchQuery=All"} +{"web_name": "Walmart Careers", "id": "Walmart Careers--7", "ques": "Filter to the Students career area, the Intern employment type and the Sam's Club brand, then open the merchandising internship in Bentonville, AR. Report the worker type chip shown on the posting and the street address listed for its location.", "web": "http://localhost:40019/", "upstream_url": "https://careers.walmart.com/us/en/results?searchQuery=All"} +{"web_name": "Walmart Careers", "id": "Walmart Careers--8", "ques": "There are Auto Care Center Technician postings at two Mississippi stores. Open both and report which store number has more open positions and how many.", "web": "http://localhost:40019/", "upstream_url": "https://careers.walmart.com/us/en/results?searchQuery=auto+care+center+technician"} +{"web_name": "Walmart Careers", "id": "Walmart Careers--9", "ques": "Two Freight Handler postings are located in Marcy, NY at different facilities. Which one has the earlier shift start time? Report its requisition ID and that start window.", "web": "http://localhost:40019/", "upstream_url": "https://careers.walmart.com/us/en/results?searchQuery=freight+handler"} +{"web_name": "Walmart Careers", "id": "Walmart Careers--10", "ques": "Compare the Senior Manager, Delivery Search, Arrival & Matching (Last Mile Delivery) postings in Bentonville, AR and Hoboken, NJ. Which one requires more years of experience under \"Option 2\" of its Minimum Qualifications? Report that posting's requisition ID and the number of years.", "web": "http://localhost:40019/", "upstream_url": "https://careers.walmart.com/us/en/jobs/R-2435546"} +{"web_name": "Walmart Careers", "id": "Walmart Careers--11", "ques": "Log in with the demo account (email: alice.j@test.com, password: TestPass123!), search for Yard Driver roles and save the Williamsburg, VA posting to your Saved roles.", "web": "http://localhost:40019/", "upstream_url": "https://careers.walmart.com/us/en/candidate-home/saved-roles"} +{"web_name": "Walmart Careers", "id": "Walmart Careers--12", "ques": "Log in as bob.c@test.com (password: TestPass123!), open your Saved roles, and remove the saved role that is at a Neighborhood Market store.", "web": "http://localhost:40019/", "upstream_url": "https://careers.walmart.com/us/en/candidate-home/saved-roles"} +{"web_name": "Walmart Careers", "id": "Walmart Careers--13", "ques": "Log in as carol.d@test.com (password: TestPass123!), apply to the Pharmacy Technician posting in Tacoma, WA using phone number 253-555-0142, and report the confirmation number shown after submitting.", "web": "http://localhost:40019/", "upstream_url": "https://careers.walmart.com/us/en/jobs/contact-details?from=%2Fus%2Fen%2Fjobs%2Fapply&lang=en&country=us"} +{"web_name": "Walmart Careers", "id": "Walmart Careers--14", "ques": "Register a new account with an email and password of your choice, then save the eCom Warehouse Worker posting at eComm Whse Logistics #9046 in Marcy, NY to your Saved roles.", "web": "http://localhost:40019/", "upstream_url": "https://careers.walmart.com/us/en/jobs/CP-9046-11101"} +{"web_name": "Walmart Careers", "id": "Walmart Careers--15", "ques": "Log in as david.k@test.com (password: TestPass123!) and update your account so the city is Rogers and the state is AR, then open My applications and report the confirmation number of your existing application.", "web": "http://localhost:40019/", "upstream_url": "https://careers.walmart.com/us/en/candidate-home"} +{"web_name": "Walmart Careers", "id": "Walmart Careers--16", "ques": "Set the location to Puerto Rico, then use the Shift filter for Weekday Day and the Rate filter for Hourly. Among the Cashier postings, report the requisition ID of the one with the most open positions and that number.", "web": "http://localhost:40019/", "upstream_url": "https://careers.walmart.com/us/en/results?searchQuery=cashier"} +{"web_name": "Walmart Careers", "id": "Walmart Careers--17", "ques": "Log in as alice.j@test.com (password: TestPass123!). Exactly one of your saved roles is a Part time job; apply to it with your account email, then report that posting's shift start window and your confirmation number.", "web": "http://localhost:40019/", "upstream_url": "https://careers.walmart.com/us/en/candidate-home/saved-roles"} +{"web_name": "Walmart Careers", "id": "Walmart Careers--18", "ques": "Open the Supply Chain and Transportation career area page, go to its Drivers category, and compare the Class A CDL Truck Driver postings at the Ottawa, KS and Williamsburg, VA facilities. For whichever of the two lists more open positions, report its requisition ID, its shift start window and that number of open positions.", "web": "http://localhost:40019/", "upstream_url": "https://careers.walmart.com/us/en/home/careers-areas/supply-chain-and-transportation"} +{"web_name": "Walmart Careers", "id": "Walmart Careers--19", "ques": "Log in as bob.c@test.com (password: TestPass123!). From the Stores and Clubs career area page, open the Digital Pickup and Delivery category, filter it to Full time roles, and open the posting with the fewest open positions. Save that role to your Saved roles, then report its requisition ID and the street address shown on the posting.", "web": "http://localhost:40019/", "upstream_url": "https://careers.walmart.com/us/en/home/careers-areas/stores-and-clubs"} diff --git a/sites/walmart_careers/templates/404.html b/sites/walmart_careers/templates/404.html new file mode 100644 index 00000000..4288a424 --- /dev/null +++ b/sites/walmart_careers/templates/404.html @@ -0,0 +1,14 @@ +{% extends "base.html" %} +{% block title %}Page not found | Walmart Careers{% endblock %} +{% block content %} +
+
+
+

We couldn't find that page

+

The role or page you were looking for isn't here. Try searching for a role instead.

+ Browse open roles + Back to home +
+
+
+{% endblock %} diff --git a/sites/walmart_careers/templates/_benefits.html b/sites/walmart_careers/templates/_benefits.html new file mode 100644 index 00000000..aa44d5b7 --- /dev/null +++ b/sites/walmart_careers/templates/_benefits.html @@ -0,0 +1,29 @@ +{# + Shared benefits block: the three coloured tiles, the two-column icon rows and a + button. The job detail page ("Benefits you'll enjoy") and the career-area pages + ("Explore our benefits") both render this partial so the two stay identical. +#} +{% macro benefits_block(heading, tiles, rows, button_label, button_href, section_id=None) -%} +
+

{{ heading }}

+
+ {% for name, headline, blurb, icon in tiles %} +
+ + {{ name }} + {{ headline }} +

{{ blurb }}

+
+ {% endfor %} +
+
+ {% for name, blurb, icon in rows %} +
+ +
{{ name }}{{ blurb }}
+
+ {% endfor %} +
+

{{ button_label }}

+
+{%- endmacro %} diff --git a/sites/walmart_careers/templates/_job_card.html b/sites/walmart_careers/templates/_job_card.html new file mode 100644 index 00000000..ad3b2eaf --- /dev/null +++ b/sites/walmart_careers/templates/_job_card.html @@ -0,0 +1,46 @@ +{# + Result card. The lines mirror the live site: a salaried posting shows only its + title, "City, ST" and the annual pay range; an hourly posting adds the + "banner #store" line, the ZIP and the shift label. Everything else (street, + requisition ID, open positions, shift window, qualifications) is detail-only. + + The whole card is clickable (stretched title link) and carries one outlined + "Select +" pill, exactly like upstream. `compact` drops the pill (trending + strips); `unsave` adds the "Saved" toggle used on the saved-roles page. +#} +{% macro job_card(job, saved_ids, compact=False, unsave=False) -%} +
+ {{ job.brand }} +
+

{{ job.title }}

+
+ {% if job.is_salaried %} +
{{ job.store.city }}, {{ job.store.state }}
+ {% else %} + {% if not job.store.is_office %} +
{{ job.store.banner }} #{{ job.store.store_number }}
+ {% endif %} +
{{ job.store.city }}, {{ job.store.state }}  {{ job.store.zip }}
+ {% endif %} +
+
+ {%- if not job.is_salaried %}{{ job.shift_label }} • {% endif -%} + {{ job.pay_range }} +
+ {% if not compact %} +
+ Select + + {% if unsave %} +
+ + + +
+ {% endif %} +
+ {% endif %} +
+
+{%- endmacro %} diff --git a/sites/walmart_careers/templates/_life.html b/sites/walmart_careers/templates/_life.html new file mode 100644 index 00000000..4535f939 --- /dev/null +++ b/sites/walmart_careers/templates/_life.html @@ -0,0 +1,23 @@ +{# + "Life at Walmart": heading, lead sentence, the two-column text / photo grid, + the full-bleed blue quote band and the closing lines. `life` is one of the + content.LIFE_AT_WALMART_* dicts; shared by both detail layouts and About Us. +#} +{% macro life_block(heading, life, section_id='life-at-walmart') -%} +
+

{{ heading }}

+

{{ life.lead }}

+
+
+ {% for paragraph in life.left %}

{{ paragraph }}

{% endfor %} +
+
+ + {% for paragraph in life.right %}

{{ paragraph }}

{% endfor %} +
+
+

{{ life.band }}

+

{{ life.closing }}

+

{{ life.note }}

+
+{%- endmacro %} diff --git a/sites/walmart_careers/templates/about.html b/sites/walmart_careers/templates/about.html new file mode 100644 index 00000000..022fe7f1 --- /dev/null +++ b/sites/walmart_careers/templates/about.html @@ -0,0 +1,56 @@ +{% extends "base.html" %} +{% from "_life.html" import life_block %} +{% block body_class %}l1{% endblock %} +{% block title %}About Us | Walmart Careers{% endblock %} +{% block content %} +
+
+

{{ content.ABOUT_HEADING }}

+

{{ content.ABOUT_BLURB }}

+
+
+ +
+
+
+ {% for heading, body in content.ABOUT_SECTIONS %} +
+

{{ heading }}

+

{{ body }}

+
+ {% endfor %} +
+
+
+ +
+ {{ life_block(content.LIFE_AT_WALMART_HEADING, content.LIFE_AT_WALMART_FIELD) }} +
+ +
+
+

Guided by our values

+
+ {% for title, blurb in content.VALUES %} +

{{ title }}

{{ blurb }}

+ {% endfor %} +
+
+
+ +
+
+

Explore our career areas

+
+ {% for area in areas %} + + {{ area.name }} + + + {% endfor %} +
+
+
+{% endblock %} diff --git a/sites/walmart_careers/templates/account.html b/sites/walmart_careers/templates/account.html new file mode 100644 index 00000000..6b4d5445 --- /dev/null +++ b/sites/walmart_careers/templates/account.html @@ -0,0 +1,22 @@ +{% extends "base.html" %} +{% block title %}My account | Walmart Careers{% endblock %} +{% block content %} +
+
+

{{ current_user.display_name }}

+
    +
  • Email{{ current_user.email }}
  • +
  • First name{{ current_user.first_name }}
  • +
  • Last name{{ current_user.last_name }}
  • +
  • Phone number{{ current_user.phone or '—' }}
  • +
  • City{{ current_user.city or '—' }}
  • +
  • State{{ current_user.state or '—' }}
  • +
  • Saved roles{{ saved_count }}
  • +
  • Applications{{ application_count }}
  • +
+ Edit profile + Saved roles + My applications +
+
+{% endblock %} diff --git a/sites/walmart_careers/templates/account_edit.html b/sites/walmart_careers/templates/account_edit.html new file mode 100644 index 00000000..d99299e3 --- /dev/null +++ b/sites/walmart_careers/templates/account_edit.html @@ -0,0 +1,41 @@ +{% extends "base.html" %} +{% block title %}Edit profile | Walmart Careers{% endblock %} +{% block content %} +
+
+

Edit your profile

+ {% if errors %} +
    {% for error in errors %}
  • {{ error }}
  • {% endfor %}
+ {% endif %} +
+ +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ + Cancel +
+
+
+{% endblock %} diff --git a/sites/walmart_careers/templates/applications.html b/sites/walmart_careers/templates/applications.html new file mode 100644 index 00000000..b9db0694 --- /dev/null +++ b/sites/walmart_careers/templates/applications.html @@ -0,0 +1,32 @@ +{% extends "base.html" %} +{% block title %}My applications | Walmart Careers{% endblock %} +{% block content %} +
+
+

My applications ({{ rows|length }})

+ {% if rows %} + + + + + + {% for row in rows %} + + + + + + + {% endfor %} + +
RoleLocationConfirmation numberStatus
{{ row.job.title }}{{ row.job.store.city }}, {{ row.job.store.state }}{{ row.confirmation_no }}{{ row.status }}
+ {% else %} +
+

No applications yet

+

Once you submit an application it shows up here with its confirmation number.

+ Browse open roles +
+ {% endif %} +
+
+{% endblock %} diff --git a/sites/walmart_careers/templates/apply_confirm.html b/sites/walmart_careers/templates/apply_confirm.html new file mode 100644 index 00000000..3da18b9b --- /dev/null +++ b/sites/walmart_careers/templates/apply_confirm.html @@ -0,0 +1,26 @@ +{% extends "base.html" %} +{% block title %}Review your application | Walmart Careers{% endblock %} +{% block content %} +
+
+
+

Review your application

+

Step 2 of 2 — check your details, then submit.

+
    +
  • Role{{ job.title }}
  • +
  • Location{{ job.store.banner }} #{{ job.store.store_number }}, {{ job.store.city }}, {{ job.store.state }}
  • +
  • Requisition ID{{ job.job_id }}
  • +
  • Email{{ draft.email }}
  • +
  • Name{{ draft.first_name }} {{ draft.last_name }}
  • +
  • Phone{{ draft.phone }}
  • +
+
+ +
+ Edit details + +
+
+
+
+{% endblock %} diff --git a/sites/walmart_careers/templates/apply_contact.html b/sites/walmart_careers/templates/apply_contact.html new file mode 100644 index 00000000..7361839b --- /dev/null +++ b/sites/walmart_careers/templates/apply_contact.html @@ -0,0 +1,49 @@ +{% extends "base.html" %} +{% block title %}Apply to {{ job.title }} | Walmart Careers{% endblock %} +{% block content %} +
+
+
+

{{ content.APPLY_HEADING }}

+

Apply: {{ job.title }} — {{ job.store.banner }} #{{ job.store.store_number }}, + {{ job.store.city }}, {{ job.store.state }} — Step 1 of 2: contact details

+ {% if errors %} +
    {% for error in errors %}
  • {{ error }}
  • {% endfor %}
+ {% endif %} +
+ +
+ + +

{{ content.APPLY_EMAIL_HINT }}

+
+
+
+ + +
+
+ + +
+
+
+ + +
+
+ +
+
+ Back to the role + +
+
+
+
+{% endblock %} diff --git a/sites/walmart_careers/templates/apply_submitted.html b/sites/walmart_careers/templates/apply_submitted.html new file mode 100644 index 00000000..add9d830 --- /dev/null +++ b/sites/walmart_careers/templates/apply_submitted.html @@ -0,0 +1,25 @@ +{% extends "base.html" %} +{% block title %}Application submitted | Walmart Careers{% endblock %} +{% block content %} +
+
+
+

Application submitted

+

Thanks, {{ application.first_name }}. Your application for {{ job.title }} at + {{ job.store.banner }} #{{ job.store.store_number }} in {{ job.store.city }}, {{ job.store.state }} + has been received.

+
Confirmation number: {{ application.confirmation_no }}
+
    +
  • Status{{ application.status }}
  • +
  • Email{{ application.email }}
  • +
  • Phone{{ application.phone }}
  • +
+
+ Keep browsing roles + {% if current_user.is_authenticated %} + My applications + {% endif %} +
+
+
+{% endblock %} diff --git a/sites/walmart_careers/templates/area.html b/sites/walmart_careers/templates/area.html new file mode 100644 index 00000000..21a9239e --- /dev/null +++ b/sites/walmart_careers/templates/area.html @@ -0,0 +1,228 @@ +{% extends "base.html" %} +{% from "_benefits.html" import benefits_block %} +{% block body_class %}l1{% endblock %} +{% block title %}{{ area.name }} careers | Walmart Careers{% endblock %} +{% block content %} +{% set page = content.AREA_PAGE.get(area.slug, content.AREA_PAGE_DEFAULT) %} +{% set hero_image = 'military-banner.png' if area.slug == 'Military' else area.hero_image %} +{% set roles_url = url_for('results', area=area.slug) if area.is_filterable else url_for('results') %} +{% set office_area = area.slug in ('technology', 'corporate') %} +{% set military = area.slug == 'Military' %} + +
+ {{ area.name }} + +
+ +{% if categories %} +
+
+

Join our team

+
+
+ + + {{ area.name }} associates + +
+ +
+
+
+{% elif military %} +{# The live Military page opens with two feature rows and three program tiles. #} +
+
+ {% for heading, blurb, cta, photo in content.MILITARY_FEATURES %} +
+ +
+

{{ heading }}

+

{{ blurb }}

+ {{ cta }} +
+
+ {% endfor %} +
+ {% for heading, blurb, photo in content.MILITARY_PROGRAMS %} + {% set target = url_for('register') if loop.last else (url_for('results', area='students', type='Intern') if loop.index == 2 else roles_url) %} + + + {{ heading }} +

{{ blurb }}

+ +
+ {% endfor %} +
+
+
+{% else %} +
+
+
+

Programs, not a job family

+

{{ area.name }} hiring runs through every career area on this site. Browse open roles and filter + by the career area, brand, shift or location that fits you.

+ Browse all open roles +
+
+
+{% endif %} + +{% set benefits_html %} +
+ {{ benefits_block('Explore our benefits', + content.benefit_tiles_for('Walmart', 'salaried' if office_area else 'hourly'), + content.JOB_BENEFIT_ROWS, 'Learn more about benefits', url_for('about_us')) }} +
+{% endset %} + +{# Field areas show the benefits right after "Join our team"; Military after its stories strip. #} +{% if not military %}{{ benefits_html }}{% endif %} + +{% if office_area %} +
+

{{ page.quote }}

+
+{% endif %} + +
+
+
+
+ + {{ page.tiles[0] }} + +
+ +
+ {% if page.tiles|length > 2 %} +
+ +
+ + {{ page.tiles[1] }} + + + {{ page.tiles[2] }} + + {% else %} + + + {{ page.tiles[1] }} + + {% endif %} +
+
+

{{ page.headline }}

+ {{ page.cta }} +
+
+
+
+ +{% if area.slug == 'stores-and-clubs' %} +{% set meet = content.MEET_STORE_COACH %} +
+
+
+
+ +
{{ meet.kicker }}{{ meet.role }}
+
+
+

Meet {{ meet.name }}

+

{{ meet.blurb }}

+
+
+
+
+{% endif %} + +{% if not office_area %} +
+

{{ page.quote }}

+
+{% endif %} + +{% if office_area %} +
+
+

{{ page.hubs_heading }}

+

{{ page.hubs_blurb }}

+ +

See all hubs

+
+
+{% else %} +
+
+

{{ content.ASSOCIATES_HEADING }}

+

{{ content.ASSOCIATES_BLURB }}

+
+ {% for name, role, _quote in page.testimonials %} +
+ +
Meet {{ name }}{{ role }}
+
+ {% endfor %} +
+ +
+
+{% endif %} + +{% if military %}{{ benefits_html }}{% endif %} + +{% if page.testimonials %} +
+
+

{{ content.INSPIRATION_HEADING }}

+
+ {% for name, role, quote in page.testimonials %} +
+ {{ name }}, {{ role }} +

“{{ quote }}”

+
+ {% endfor %} +
+ +
+
+{% endif %} + +
+
+

Find the role that's a perfect fit.

+ +
+
+{% endblock %} diff --git a/sites/walmart_careers/templates/base.html b/sites/walmart_careers/templates/base.html new file mode 100644 index 00000000..69094570 --- /dev/null +++ b/sites/walmart_careers/templates/base.html @@ -0,0 +1,174 @@ + + + + + + {% block title %}Careers at Walmart{% endblock %} + + + + + + + +
+ {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} +
+ {% for category, message in messages %} +
{{ message }}
+ {% endfor %} +
+ {% endif %} + {% endwith %} + {% block content %}{% endblock %} +
+ +
+
+ + {# The live footer draws each harvested (#151F29) glyph on a 44px white disc. #} + + {% set rights = 'applicant rights under Federal Employment Laws.' %} + + + +
+
+ + diff --git a/sites/walmart_careers/templates/base_auth.html b/sites/walmart_careers/templates/base_auth.html new file mode 100644 index 00000000..feca2286 --- /dev/null +++ b/sites/walmart_careers/templates/base_auth.html @@ -0,0 +1,35 @@ +{# + Stripped layout for sign-in / register, mirroring identity.walmart.com: + a centred spark, no navigation, and a light link footer. +#} + + + + + + {% block title %}Sign in | Walmart Careers{% endblock %} + + + + +
+ + + + {% with messages = get_flashed_messages(with_categories=true) %} + {% for category, message in messages %} +
{{ message }}
+ {% endfor %} + {% endwith %} + {% block content %}{% endblock %} +
+
+ {{ content.AUTH_COPYRIGHT }} + +
+ + diff --git a/sites/walmart_careers/templates/hiring_process.html b/sites/walmart_careers/templates/hiring_process.html new file mode 100644 index 00000000..5f9bae34 --- /dev/null +++ b/sites/walmart_careers/templates/hiring_process.html @@ -0,0 +1,70 @@ +{% extends "base.html" %} +{% from "_job_card.html" import job_card %} +{% block body_class %}l1{% endblock %} +{% block title %}How we hire | Walmart Careers{% endblock %} +{% block content %} +
+ +
+

{{ content.HIRING_HEADING }}

+

{{ content.HIRING_BLURB }}

+ {{ content.HIRING_HERO_CTA }} +
+
+ +
+
+

Explore something new

+
+ {% for title, blurb in content.HIRING_STEPS %} +

{{ title }}

{{ blurb }}

+ {% endfor %} +
+
+
+ +{% for heading, questions in content.HIRING_FAQ %} + {% set intro = content.HIRING_FAQ_INTROS[loop.index0] %} +
+
+
+
+ +

{{ heading }}

+

{{ intro[0] }}

+
+
+ {% for question, answer in questions %} +
+ {{ question }} +

{{ answer }}

+
+ {% endfor %} +
+
+
+
+{% endfor %} + +
+
+
+ +
+

{{ content.HIRING_SIMULATOR.heading }}

+

{{ content.HIRING_SIMULATOR.blurb }}

+ {{ content.HIRING_SIMULATOR.cta }} +
+
+
+
+ + +{% endblock %} diff --git a/sites/walmart_careers/templates/index.html b/sites/walmart_careers/templates/index.html new file mode 100644 index 00000000..27442668 --- /dev/null +++ b/sites/walmart_careers/templates/index.html @@ -0,0 +1,186 @@ +{% extends "base.html" %} +{% block body_class %}l1{% endblock %} +{% from "_job_card.html" import job_card %} +{% block title %}Careers at Walmart{% endblock %} +{% block content %} +
+
+

{{ content.HERO_HEADLINE_1 }}{{ content.HERO_HEADLINE_2 }}

+ +
+
+ Associates in a home office space + + Associates in a distribution center +
+
+ +
+
+

Trending roles

+
+ {% for job in trending %}{{ job_card(job, saved_ids, compact=True) }}{% endfor %} +
+
+
+ +
+
+
+ {% for area in ribbon_areas %} + + {{ area.name }} + + + {% endfor %} +
+
+
+ +
+
+
+
+ + Grow your career here + + +
+ An associate helping a customer in the grocery aisle +
+
+ +
+
+ A delivery drone in flight +
+ + People-led. Tech-powered. + + + + Guided by our values + + +
+ The Walmart home office campus +
+
Strive for excellence
+
+ +
+
+ +
+
Respect for the individual
+ + Sam's Club + + +
+ A Member Services associate at Sam's Club +
+
+
+

{{ content.HOME_INTRO_HEADLINE[0] }}
{{ content.HOME_INTRO_HEADLINE[1] }}

+ {{ content.HOME_INTRO_CTA }} +
+
+
+
+ +
+
+

Explore our Benefits

+
+
+ {% for name, blurb, icon in content.BENEFIT_ROWS %} +
+ + {{ name }} + {{ blurb }} +
+ {% endfor %} +
+
+

{{ content.BENEFITS_ASIDE }}

+ {{ content.BENEFITS_CTA }} +
+
+
+
+ +
+
+
+

{{ content.MILESTONE_HEADING }}

+
+ {% for sentence, label, style in content.MILESTONE_BADGES %} +
+ {% if loop.index is even %}{% endif %} +
+
{% if label %}{{ label }}{% endif %}
+
{{ sentence }}
+ {% if label != '10 YEARS' %}
Walmart
{% else %}
{{ label }}
{% endif %} +
+ {% if loop.index is odd %}{% endif %} +
+ {% endfor %} +
+
+
+
+ +
+
+

{{ content.ASSOCIATES_HEADING }}

+

{{ content.ASSOCIATES_BLURB }}

+
+ {% for role, kicker in [('Store Coach', 'Day in the life'), + ('Optician', 'Day in Life'), + ('Store Manager', 'Day in the life')] %} +
+ +
{{ kicker }}{{ role }} +
+
+ {% endfor %} +
+ +
+
+ +
+
+

{{ content.FIND_ROLE_HEADING }}

+ +
+
+{% endblock %} diff --git a/sites/walmart_careers/templates/job_detail.html b/sites/walmart_careers/templates/job_detail.html new file mode 100644 index 00000000..c91e416a --- /dev/null +++ b/sites/walmart_careers/templates/job_detail.html @@ -0,0 +1,246 @@ +{% extends "base.html" %} +{% from "_job_card.html" import job_card %} +{% from "_benefits.html" import benefits_block %} +{% from "_life.html" import life_block %} +{% block title %}{{ job.title }} in {{ job.store.city }}, {{ job.store.state }} | Walmart Careers{% endblock %} +{% block content %} +{% set salaried = job.population == 'salaried' %} + +{# + Both populations use the live three-photo masthead; the identity card in the + middle column is solid ld-blue for salaried postings and blue-over-navy for + hourly ones, exactly as reference/job_detail_corp.png vs reference/job_detail.png. +#} +
+
+
+
+
+ Walmart spark +
+ {% if job.hero_images|length > 0 %} + + {% endif %} +
+
+ {% if job.hero_images|length > 1 %} + + {% endif %} +
+
+ + +
+
+
{{ job.title }}
+
{{ job.store.city }}, {{ job.store.state }}
+
+
+ + + + {% if is_saved %} +
+ + + +
+ {% else %} +
+ + + +
+ {% endif %} + Apply now + +
+
+
+ {% if job.hero_images|length > 2 %} + + {% endif %} +
+
+
+ +{# + Below the masthead the live page is one two-column layout: the sticky + "On this page" panel on the left, and on the right the role details, the + benefits block and "Life at Walmart" one after another in the same column. +#} +
+ + +
+
+

{{ job.title }}

+ +
+
+ {% if not job.store.is_office %} + + {% endif %} +
+ {{ job.store.street }}
+ {{ job.store.city }}, {{ job.store.state }} {{ job.store.zip }} +
+ {% if job.positions_available %} +
{{ job.positions_available }} open position{{ '' if job.positions_available == 1 else 's' }}
+ {% endif %} + {{ job.job_id }} +
+
{{ map_svg|safe }}
+
+ +
+ + + {{ job.pay_range }}{% if job.min_age_note %}*{% endif %} + + {% if salaried %} + + + {{ job.worker_type }} + + + + Salaried + + {% else %} + + + {{ job.employment_type }} + + + + {{ job.shift_time or job.shift_label }} + + {% endif %} +
+ {% if job.min_age_note %} +

* Must be at least 18 years old

+ {% endif %} + +
+ {% if salaried %} +

Position Summary...

+

{{ job.summary }}

+

What you'll do...

+ {% for paragraph in job.description.split('\n\n') %}

{{ paragraph }}

{% endfor %} + {% if job.about_team %} +

About the team:

+

{{ job.about_team }}

+ {% endif %} + {% if job.additional_description %} +

What you'll bring:

+
    + {% for bullet in job.additional_description %}
  • {{ bullet }}
  • {% endfor %} +
+ {% endif %} + {% set about = content.SALARIED_ABOUT_AREA.get(job.area.slug, content.SALARIED_ABOUT_DEFAULT) %} +

{{ about[0] }}

+

{{ about[1] }}

+ {% if job.store.is_office %} +

Flexible, hybrid work:

+

{{ content.SALARIED_HYBRID_NOTE }}

+ {% endif %} +

Benefits:

+

{{ content.SALARIED_BENEFITS_NOTE }}

+

Equal Opportunity Employer:

+

{{ content.SALARIED_EEO_NOTE }}

+

{{ content.SALARIED_SCOPE_NOTE }}

+

{{ content.SALARIED_PAY_NOTE }} + The annual salary range for this position is {{ job.pay_range }}. + Additional compensation includes annual or quarterly performance bonuses.

+

Minimum Qualifications...

+

{{ content.MIN_QUAL_PREAMBLE }}

+
    + {% for option in job.min_qualifications %}
  • {{ option }}
  • {% endfor %} +
+

Preferred Qualifications...

+

{{ content.PREF_QUAL_PREAMBLE }}

+

{{ job.preferred_qualifications }}

+

Primary Location...

+

{{ job.store.street }}, {{ job.store.city }}, {{ job.store.state }} {{ job.store.zip }}, + United States of America

+ {% else %} +

Role summary

+

{{ job.summary }}

+

What you'll do

+ {% for paragraph in job.description.split('\n\n') %}

{{ paragraph }}

{% endfor %} +

What you'll bring

+
    + {% for bullet in job.additional_description %}
  • {{ bullet }}
  • {% endfor %} +
+ {% if job.hashtag %} +

{{ job.hashtag }}

+ {% endif %} + + {% endif %} + +
+
+ + {{ benefits_block("Benefits you'll enjoy", benefit_tiles, content.JOB_BENEFIT_ROWS, + 'Learn more', url_for('resources_hiring'), section_id='benefits') }} + + {{ life_block(content.LIFE_AT_WALMART_HEADING, + content.LIFE_AT_WALMART_CORP if salaried else content.LIFE_AT_WALMART_FIELD) }} +
+
+ +{% if related %} + +{% endif %} +{% endblock %} diff --git a/sites/walmart_careers/templates/locations.html b/sites/walmart_careers/templates/locations.html new file mode 100644 index 00000000..ca6268b8 --- /dev/null +++ b/sites/walmart_careers/templates/locations.html @@ -0,0 +1,56 @@ +{% extends "base.html" %} +{% block body_class %}l1{% endblock %} +{% block title %}Our locations | Walmart Careers{% endblock %} +{% block content %} +
+
+ +

{{ content.LOCATIONS_HEADING }}

+ +
+
+
+
+

{{ content.LOCATIONS_BLURB }}

+
+
+
+
+

Hubs around the world

+
+ {% for hub in hubs %} +
+ {{ hub.hub_name or hub.city }} + {{ hub.hub_name or hub.city }} +

{{ hub.hub_blurb or 'A Walmart hub location.' }}

+ + + +
+ {% endfor %} +
+
+
+
+

{{ content.LOCATIONS_CLOSING }}

+
+
+
+

{{ content.FIND_ROLE_HEADING }}

+ +
+
+{% endblock %} diff --git a/sites/walmart_careers/templates/login.html b/sites/walmart_careers/templates/login.html new file mode 100644 index 00000000..a3bc1e38 --- /dev/null +++ b/sites/walmart_careers/templates/login.html @@ -0,0 +1,25 @@ +{% extends "base_auth.html" %} +{% block title %}Sign in | Walmart Careers{% endblock %} +{% block content %} +
+

Sign in or create your account

+

Sign in with the email and password on your candidate account.

+ {% if errors %} +
    {% for error in errors %}
  • {{ error }}
  • {% endfor %}
+ {% endif %} +
+ + {% if next_url %}{% endif %} +
+ + +
+
+ + +
+ +
+

New here? Create a candidate account.

+
+{% endblock %} diff --git a/sites/walmart_careers/templates/register.html b/sites/walmart_careers/templates/register.html new file mode 100644 index 00000000..e0b8d5c9 --- /dev/null +++ b/sites/walmart_careers/templates/register.html @@ -0,0 +1,36 @@ +{% extends "base_auth.html" %} +{% block title %}Create an account | Walmart Careers{% endblock %} +{% block content %} +
+

Create your candidate account

+ {% if errors %} +
    {% for error in errors %}
  • {{ error }}
  • {% endfor %}
+ {% endif %} +
+ + {% if next_url %}{% endif %} +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+

Already have an account? Sign in.

+
+{% endblock %} diff --git a/sites/walmart_careers/templates/results.html b/sites/walmart_careers/templates/results.html new file mode 100644 index 00000000..04bb8f42 --- /dev/null +++ b/sites/walmart_careers/templates/results.html @@ -0,0 +1,208 @@ +{% extends "base.html" %} +{% from "_job_card.html" import job_card %} +{% block title %}{{ total }} open roles | Walmart Careers{% endblock %} +{% block content %} +
+
+ + +
+ + + {% if filters.tab == 'future' %} +
+

No future roles here

+

{{ content.EMPTY_FUTURE_ROLES }}

+ Back to open roles +
+ {% elif filters.tab == 'content' %} +
+

No content results

+

{{ content.EMPTY_CONTENT_TAB }}

+ Back to open roles +
+ {% else %} + +
+

{{ "{:,}".format(total) }} open role{{ '' if total == 1 else 's' }}

+
+ + + Add your location + +
+

Location

+ {% if filters.q %}{% endif %} + {% for key in ['area','category','brand','shift','type','rate'] %} + {% for value in filters[key] %}{% endfor %} + {% endfor %} +
+ + +
+ {% for radius in radius_values %} + + {% endfor %} +

A state or territory name (for example Puerto Rico or + PR) returns every role in that state and ignores the radius.

+
+ Reset + +
+
+
+
+ + {% if location_failed %} +

We couldn't find that location.

+ {% elif location %} +

+ {% if location.kind == 'state' %} + Showing roles in {{ location.label }} ({{ location.state }}). + {% else %} + Showing roles within {{ filters.radius }} miles of {{ location.label }}. + {% endif %} +

+ {% endif %} + +
+
+ + + Filters + +
+

Filters

+ {% if filters.q %}{% endif %} + {% if filters.loc %} + + + {% endif %} +
+
+ Brand + {% for value in brand_values %} + + {% endfor %} +
+
+ Shift + {% for value in shift_values %} + + {% endfor %} +
+
+ Employment Type + {% for value in employment_type_values %} + + {% endfor %} + Rate + {% for value in rate_values %} + + {% endfor %} +
+
+ Career Area + {% for area in areas %} + +
+ Categories in {{ area.name }} + {% for category in area.categories %} + + {% endfor %} +
+ {% endfor %} +
+
+
+ Reset + +
+
+
+ +
+ Sort by: {{ 'Relevance' if filters.sort == 'relevance' else 'Most recent' }} + + +
+
+ + {% if filter_chips %} +
+ {% for chip in filter_chips %} + {{ chip.label }} × + {% endfor %} + Clear all +
+ {% endif %} + + {% if jobs %} +
+ {% for job in jobs %}{{ job_card(job, saved_ids) }}{% endfor %} +
+ {% if pages > 1 %} + + {% endif %} + {% else %} +
+

No roles matched

+

Try a different keyword, widen your location radius, or clear a filter.

+ Reset all filters +
+ {% endif %} + {% endif %} +
+
+
+{% endblock %} diff --git a/sites/walmart_careers/templates/saved_roles.html b/sites/walmart_careers/templates/saved_roles.html new file mode 100644 index 00000000..60298bc4 --- /dev/null +++ b/sites/walmart_careers/templates/saved_roles.html @@ -0,0 +1,52 @@ +{% extends "base.html" %} +{% block body_class %}l1{% endblock %} +{% from "_job_card.html" import job_card %} +{% block title %}Saved roles | Walmart Careers{% endblock %} +{% block content %} +
+
+
+
+

{{ content.SAVED_PROMO_HEADLINE[0] }}
{{ content.SAVED_PROMO_HEADLINE[1] }}

+

{{ content.SAVED_PROMO_BLURB }}

+ {% if current_user.is_authenticated %} + See all open roles + {% else %} + Sign in or create account + {% endif %} +
+ A Walmart associate outside a store +
+
+
+ +
+
+

Saved roles ({{ rows|length }})

+ {% if not current_user.is_authenticated %} +

{{ content.SAVED_EMPTY_NOTE }} + Sign in to see the roles you have + saved, search for a role, or view your recommended roles below.

+ {% elif not rows %} +

{{ content.SAVED_EMPTY_NOTE }} + Search for a role, or view your recommended roles below.

+ {% else %} +
+ {% for row in rows %}{{ job_card(row.job, saved_ids, unsave=True) }}{% endfor %} +
+ {% endif %} +
+
+{# Recommendations only fill the page when there is nothing saved to show. #} +{% if not current_user.is_authenticated or not rows %} + +{% endif %} +{% endblock %} diff --git a/sites/walmart_careers/templates/terms.html b/sites/walmart_careers/templates/terms.html new file mode 100644 index 00000000..76bfad42 --- /dev/null +++ b/sites/walmart_careers/templates/terms.html @@ -0,0 +1,14 @@ +{% extends "base.html" %} +{% block title %}Terms & Conditions | Walmart Careers{% endblock %} +{% block content %} +
+
+

{{ content.TERMS_HEADING }}

+ {% for heading, body in content.TERMS_SECTIONS %} +

{{ heading }}

+

{{ body }}

+ {% endfor %} + +
+
+{% endblock %} diff --git a/websyn_start.sh b/websyn_start.sh index c347ccec..f1cf87f2 100644 --- a/websyn_start.sh +++ b/websyn_start.sh @@ -5,7 +5,8 @@ set -e SITES=(allrecipes amazon apple arxiv bbc_news booking github google_flights google_map google_search huggingface wolfram_alpha - cambridge_dictionary coursera espn merriam_webster ikea phys_org target) + cambridge_dictionary coursera espn merriam_webster ikea phys_org target + walmart_careers) BASE_PORT=40000 PID_DIR=/tmp/websyn_pids mkdir -p "$PID_DIR"