// ============== jobs board ==============
function Jobs({ go, focusId }) {
const { CHAIN_FILTERS, TYPE_FILTERS, LEVEL_FILTERS, REMOTE_FILTERS, useRoles } = window.W3J;
const { roles: ROLES, loading, source } = useRoles();
const [q, setQ] = React.useState("");
const [chain, setChain] = React.useState(new Set());
const [cat, setCat] = React.useState(new Set());
const [level, setLevel] = React.useState(new Set());
const [remote, setRemote] = React.useState(new Set());
const [minComp, setMinComp] = React.useState(0);
const [sort, setSort] = React.useState("recent");
const [active, setActive] = React.useState(null);
React.useEffect(() => {
if (focusId) {
const r = ROLES.find((x) => x.id === focusId);
if (r) setActive(r);
}
}, [focusId, ROLES]);
const toggle = (set, setSet, val) => {
const next = new Set(set);
if (next.has(val)) next.delete(val); else next.add(val);
setSet(next);
};
const filtered = React.useMemo(() => {
const remoteMatch = (r) => {
if (remote.size === 0) return true;
return [...remote].some((rk) => r.remote.toLowerCase().startsWith(rk.toLowerCase()));
};
let list = ROLES.filter((r) => {
if (q) {
const hay = (r.title + " " + r.company + " " + r.stack.join(" ") + " " + r.chain + " " + r.cat).toLowerCase();
if (!hay.includes(q.toLowerCase())) return false;
}
if (chain.size && !chain.has(r.chain)) return false;
if (cat.size && !cat.has(r.cat)) return false;
if (level.size && !level.has(r.level)) return false;
if (!remoteMatch(r)) return false;
if (r.min < minComp) return false;
return true;
});
if (sort === "recent") list = list.sort((a, b) => a.posted - b.posted);
if (sort === "comp") list = list.sort((a, b) => b.max - a.max);
if (sort === "title") list = list.sort((a, b) => a.title.localeCompare(b.title));
return list;
}, [q, chain, cat, level, remote, minComp, sort, ROLES]);
const counts = React.useMemo(() => {
const c = (key) => Object.fromEntries(
[...new Set(ROLES.map((r) => r[key]))].map((v) => [v, ROLES.filter((r) => r[key] === v).length])
);
return { chain: c("chain"), cat: c("cat"), level: c("level") };
}, [ROLES]);
const clearAll = () => {
setQ(""); setChain(new Set()); setCat(new Set()); setLevel(new Set()); setRemote(new Set()); setMinComp(0);
};
const totalActive = chain.size + cat.size + level.size + remote.size + (q ? 1 : 0) + (minComp > 0 ? 1 : 0);
return (
The job board · {ROLES.length} live roles{source === "mock" && " (preview)"}
Open roles.
Every role here is actively retained by Web3JOE. Companies are anonymized in public listings,
full briefs are sent to qualified candidates after a short call.
setQ(e.target.value)} />
{q && setQ("")} style={{ color: "var(--fg-dim)", padding: 4 }}> }
{filtered.length} / {ROLES.length} roles
Sort
setSort(e.target.value)}>
Most recent
Highest comp
A → Z
{loading ? (
) : filtered.length === 0 ? (
No matches.
Try widening your filters — or clear all .
) : (
{filtered.map((r) => (
setActive(r)}>
{r.id}
{r.title}
{r.company} · {r.remote} · posted {r.posted}d ago
{r.chain}
{r.stack.slice(0, 2).map((s) => {s} )}
$ {r.min}–{r.max}k
))}
)}
setActive(null)} go={go} />
);
}
function FilterGroup({ title, opts, active, onToggle, counts }) {
return (
{title}
{opts.map((c) => (
onToggle(c)}>
{c}
{counts && {counts[c] || 0} }
))}
);
}
function JobDrawer({ role, onClose, go }) {
const { user } = useAuth();
const [applied, setApplied] = React.useState(false);
const [applying, setApplying] = React.useState(false);
const [err, setErr] = React.useState("");
const [showApplyForm, setShowApplyForm] = React.useState(false);
const [profile, setProfile] = React.useState(null);
const [form, setForm] = React.useState(blankForm());
const [answers, setAnswers] = React.useState({});
const [resumeMode, setResumeMode] = React.useState("profile"); // "profile" | "upload"
const [uploadedKey, setUploadedKey] = React.useState(null);
const [uploading, setUploading] = React.useState(false);
const fileInputRef = React.useRef(null);
React.useEffect(() => {
const onKey = (e) => { if (e.key === "Escape") onClose(); };
if (role) window.addEventListener("keydown", onKey);
return () => window.removeEventListener("keydown", onKey);
}, [role]);
React.useEffect(() => {
setApplied(false); setShowApplyForm(false); setErr(""); setAnswers({});
setResumeMode("profile"); setUploadedKey(null);
}, [role?.id]);
React.useEffect(() => {
if (!showApplyForm || !user || user.role !== "candidate") return;
window.W3J_API.apiFetch("/candidates/me").then((p) => {
setProfile(p);
setForm({
full_name: p.full_name || "",
email: user.email || "",
linkedin_url: p.linkedin_url || "",
twitter_url: p.twitter_url || "",
location: p.location || "",
expected_comp: "",
});
setResumeMode(p.resume_key ? "profile" : "upload");
}).catch(() => {});
}, [showApplyForm, user]);
const setField = (k) => (e) => setForm((f) => ({ ...f, [k]: e.target.value }));
const setAnswer = (qid) => (e) => setAnswers((a) => ({ ...a, [qid]: e.target.value }));
const uploadResume = async (file) => {
if (!file) return;
setErr(""); setUploading(true);
try {
const fd = new FormData();
fd.append("file", file);
const res = await window.W3J_API.apiFetch("/uploads/application-resume", {
method: "POST", body: fd, isForm: true,
});
setUploadedKey(res.resume_key);
} catch (ex) {
setErr(ex.message || "upload failed");
} finally {
setUploading(false);
}
};
const apply = async (e) => {
if (e) e.preventDefault();
setErr(""); setApplying(true);
try {
if (!role?.uuid) throw new Error("This is a preview role. Roles will be live once the recruiter creates them.");
const resume_key =
resumeMode === "upload" ? uploadedKey
: (profile?.resume_key || null);
if (!resume_key) {
throw new Error("Please attach a resume — upload here or via your dashboard.");
}
const body = {
role_id: role.uuid,
full_name: form.full_name,
email: form.email,
linkedin_url: form.linkedin_url,
twitter_url: form.twitter_url || null,
location: form.location,
expected_comp: form.expected_comp || null,
resume_key,
answers,
};
await window.W3J_API.apiFetch("/applications", { method: "POST", body });
setApplied(true);
} catch (ex) {
if (ex.status === 409) setApplied(true);
else setErr(ex.message);
} finally {
setApplying(false);
}
};
const questions = role?.questions || [];
return (
<>
{role && (
<>
{role.id} · {role.company}
{role.title}
{role.chain && {role.chain} }
{role.stack.map((s) => {s} )}
Total comp
${role.min}–${role.max}k + tokens
{role.description && (<>
About the role {role.description}
>)}
{role.requirements && role.requirements.length > 0 ? (
<>
What we're looking for
{role.requirements.map((s, i) => {s} )} >
) : (
<>
About the company
A {role.company.toLowerCase()} operating at the frontier of {role.cat.toLowerCase()}. Backed by tier-1 funds, profitable, and shipping product to mainnet weekly. Full brief shared with shortlisted candidates after intro call.
>
)}
{role.offer && role.offer.length > 0 ? (
<>
What's on offer
{role.offer.map((s, i) => {s} )} >
) : (
<>
What's on offer
Base ${role.min}–${role.max}k + meaningful token allocation.
4-year vesting with 1-year cliff. Acceleration clauses negotiable.
Top-tier health, vision, dental coverage globally.
Annual onsites in Lisbon, NYC, Singapore, Dubai.
>
)}
{showApplyForm && !applied && (
)}
APPLICATIONS REVIEWED WITHIN 48H
{applied ? (
Applied ✓
) : !user ? (
go("login")}>Sign in to apply
) : user.role !== "candidate" ? (
Recruiter view
) : !showApplyForm ? (
setShowApplyForm(true)}>Apply for this role
) : (
{applying ? "…" : "Submit application"} {!applying && }
)}
>
)}
>
);
}
function blankForm() {
return {
full_name: "", email: "", linkedin_url: "", twitter_url: "",
location: "", expected_comp: "",
};
}
window.Jobs = Jobs;