/* global React, Icon, Origin, SyncChip, Meter, CB, RoleChip, JobChip, Portrait, Tag, Empty, Seg, Input, useRoute */ // ============================================================ // Screens 5-7: Static roster, Raid night (FFLogs), Loot table // ============================================================ const { useState: useS57, useMemo: useM57 } = React; const STATIC_MEMBERS = [ { name: 'Aerith Nightsong', job: 'PLD', alt: 'WAR', synced: '2h ago' }, { name: 'Korr Brassbottom', job: 'WAR', alt: 'GNB', synced: '4h ago' }, { name: 'Liliane d\'Aubreville', job: 'WHM', alt: 'AST', synced: '1d ago' }, { name: 'Yumi Shiroyama', job: 'SCH', alt: 'SGE', synced: '2h ago' }, { name: 'Veska Voidsong', job: 'NIN', alt: 'VPR', synced: '3h ago' }, { name: 'Olric Stonebreak', job: 'SAM', alt: 'DRG', synced: '6h ago' }, { name: 'Thalia Greycloak', job: 'BRD', alt: 'DNC', synced: '2h ago' }, { name: 'Imre Kalvain', job: 'BLM', alt: 'PCT', synced: '12h ago' }, ]; const SUBS = [ { name: 'Mira Sundwalker', job: 'AST', alt: 'WHM', synced: '1d ago' }, { name: 'Brennan Holt', job: 'DRK', alt: 'PLD', synced: '3d ago' }, ]; // -------------------- 5. Static — Roster -------------------- function ScreenStaticRoster({ empty }) { const { go } = useRoute(); if (empty) { return (

Static

Your raid group's home — roster, schedule, raid nights, loot, prog.
} title="No static yet" sub="Create a static to start tracking attendance, prog, and loot. You can invite up to 12 members (8 main + subs)." action={
} />
); } return (
{/* Static header */}
Active M4S — Savage Phase 3 prog

Aether Crystals

Tue / Thu · 8:00 – 11:00 PM ET · 4 weeks until tier ends
Prog phase
P3
Avg attendance
94%
last 8 nights ·
Next raid
Tue 8:00 PM
in 2d 14h · auto reminder
Composition · 2 · 2 · 4
{/* 2-2-4 composition */}
Tanks 2
{STATIC_MEMBERS.filter(m => m.job === 'PLD' || m.job === 'WAR' || m.job === 'DRK' || m.job === 'GNB').map(m => ( ))}
Healers 2
{STATIC_MEMBERS.filter(m => ['WHM','SCH','AST','SGE'].includes(m.job)).map(m => ( ))}
DPS 4
{STATIC_MEMBERS.filter(m => ['NIN','SAM','BRD','BLM','DRG','MNK','RPR','VPR','MCH','DNC','SMN','RDM','PCT'].includes(m.job)).map(m => ( ))}
{/* Subs */}
Subs · {SUBS.length}
{SUBS.map(m => )}
Open sub slot
); } function MemberCard({ m, sub }) { return (
{m.alt && alt {m.alt}} {sub && sub}
{m.name}
 synced {m.synced}
); } // -------------------- 6. Raid Night — FFLogs import -------------------- function ScreenRaidNight({ imported }) { const { go } = useRoute(); const [link, setLink] = useS57(imported ? 'https://www.fflogs.com/reports/aBC123xYz9KqMnPp' : ''); const [state, setState] = useS57(imported ? 'done' : 'empty'); // empty | importing | done const attendance = [ { name: 'Aerith Nightsong', job: 'PLD', present: true, pulls: 18, best: 62, manual: false }, { name: 'Korr Brassbottom', job: 'WAR', present: true, pulls: 18, best: 62, manual: false }, { name: 'Liliane d\'Aubreville', job: 'WHM', present: true, pulls: 18, best: 62, manual: false }, { name: 'Yumi Shiroyama', job: 'SCH', present: true, pulls: 18, best: 62, manual: false }, { name: 'Veska Voidsong', job: 'NIN', present: true, pulls: 18, best: 62, manual: false }, { name: 'Olric Stonebreak', job: 'SAM', present: false, pulls: 0, best: 0, manual: true, note: 'Excused' }, { name: 'Thalia Greycloak', job: 'BRD', present: true, pulls: 18, best: 62, manual: false }, { name: 'Mira Sundwalker', job: 'AST', present: true, pulls: 12, best: 62, manual: false, isSub: true, swapsFor: 'OLR' }, { name: 'Imre Kalvain', job: 'BLM', present: true, pulls: 6, best: 41, manual: false, lateNote: 'Joined late · pull 13' }, ]; return (
Tuesday · March 4 · 8:00–11:00 PM ET Raid night

Raid night — M4S phase 3

Paste an FFLogs report below. We'll auto-fill attendance and pull the prog phase from the log.
{/* Import bar */}
FFLogs report
} placeholder="https://www.fflogs.com/reports/..." value={link} onChange={e => setLink(e.target.value)} />
{state === 'empty' && ( )} {state === 'importing' && ( )} {state === 'done' && ( <> )}
{state === 'empty' && (
} title="Paste an FFLogs link to auto-fill attendance" sub="We'll match each report participant to a member, mark presence, and pull the highest phase reached. You can override any row manually afterward." action={
Tip: try posting your report to /raid log in Discord — the bot will import it for you.
} />
)} {state !== 'empty' && ( <> {/* Import summary */}
Phase reached
P3 · 62%
previous best 58% ·
Pulls
18
3h 02m total ·
Present
7 / 8
1 absence · 1 sub-in ·
Best pull
62%
pull 14 · 4m 12s
{/* Attendance list */}
Attendance · auto-filled from log
{attendance.map(a => ( ))}
Present Member Job Pulls Best phase Source
{}} />
{a.name}
{a.isSub &&
sub-in for {a.swapsFor}
} {a.lateNote &&
{a.lateNote}
} {a.note &&
{a.note}
}
{a.pulls} {a.best ? `${a.best}%` : '—'} {a.manual ? : }
Manual overrides are kept separate
Re-importing the same report won't overwrite rows you marked manually. Auto rows still update if FFLogs revises the report.
)}
); } // -------------------- 7. Static — Loot table -------------------- function ScreenLoot() { const { go } = useRoute(); const drops = [ { item: 'Crystalline ring of fending', night: 'Tue · Mar 4', to: 'Aerith Nightsong', toJob: 'PLD', rule: 'BiS priority', bisCount: 9 }, { item: 'Crystalline trousers of healing', night: 'Tue · Mar 4', to: 'Liliane d\'Aubreville', toJob: 'WHM', rule: 'BiS priority', bisCount: 11 }, { item: 'Sacred mythril (token)', night: 'Tue · Mar 4', to: 'Korr Brassbottom', toJob: 'WAR', rule: 'Free roll', bisCount: 8 }, { item: 'Crystalline gloves of striking', night: 'Thu · Feb 27', to: 'Veska Voidsong', toJob: 'NIN', rule: 'BiS priority', bisCount: 7 }, { item: 'Crystalline earring of casting', night: 'Thu · Feb 27', to: 'Imre Kalvain', toJob: 'BLM', rule: 'BiS priority', bisCount: 6 }, { item: 'Sacred mythril (token)', night: 'Thu · Feb 27', to: 'Thalia Greycloak', toJob: 'BRD', rule: 'Free roll', bisCount: 5 }, { item: 'Crystalline shoes of aiming', night: 'Tue · Feb 25', to: 'Olric Stonebreak', toJob: 'SAM', rule: 'BiS priority', bisCount: 4 }, ]; const needs = [ { member: 'Aerith Nightsong', job: 'PLD', remaining: 3, items: ['Earring','Bracelet','Ring'] }, { member: 'Korr Brassbottom', job: 'WAR', remaining: 5, items: ['MH','Earring','Bracelet','Ring','Necklace'] }, { member: 'Liliane d\'Aubreville', job: 'WHM', remaining: 1, items: ['Necklace'] }, { member: 'Yumi Shiroyama', job: 'SCH', remaining: 4, items: ['Body','Hands','Feet','Earring'] }, { member: 'Veska Voidsong', job: 'NIN', remaining: 4, items: ['Body','Head','Ring','Necklace'] }, { member: 'Olric Stonebreak', job: 'SAM', remaining: 6, items: ['MH','Head','Body','Hands','Earring','Ring'] }, { member: 'Thalia Greycloak', job: 'BRD', remaining: 5, items: ['MH','Head','Body','Earring','Necklace'] }, { member: 'Imre Kalvain', job: 'BLM', remaining: 6, items: ['MH','Head','Body','Hands','Earring','Necklace'] }, ]; return (
Aether Crystals · M4S Tier 2 of 3

Loot table

Who got what, when, and under what rule. All entries are manual — including the bot's `/loot` records.
{/* Discord tip callout */}
Log drops live in Discord
Run /loot <item> <member> <rule> in your raid channel — the bot pushes the row straight into this table.
{/* Drops table */}
Recent drops
{drops.length} drops this tier
} placeholder="Search item or member…" style={{ width: 220 }} />
{drops.map((d, i) => ( ))}
Item Raid night Awarded to Rule Source
{d.item.includes('token') ? : }
{d.item}
{d.night}
{d.to}
{d.rule}
{/* Who still needs what */}
Who still needs what
    {needs.map(n => (
  • {n.member}
    {n.items.map(it => {it})}
    {n.remaining}/14
  • ))}
Computed by diffing each member's BiS list against awarded drops.
); } Object.assign(window, { ScreenStaticRoster, ScreenRaidNight, ScreenLoot });