Add modal name popup when a Pi-Rat earns a Name
When a Pi-Rat completes their second objective and ranks up, they now get a blocking modal popup (NameModal.svelte, rendered by Dashboard across all phases) to claim their Pirate Name, rather than an optional box buried in their character sheet. Removed the old inline prompt-box and its handler from CharacterSheet.svelte. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
88
frontend/src/components/NameModal.svelte
Normal file
88
frontend/src/components/NameModal.svelte
Normal file
@@ -0,0 +1,88 @@
|
||||
<script>
|
||||
import { apiRequest } from '../lib/api';
|
||||
|
||||
export let state;
|
||||
|
||||
let newNameInput = '';
|
||||
let error = '';
|
||||
let submitting = false;
|
||||
|
||||
async function submitNewName() {
|
||||
if (!newNameInput.trim() || submitting) return;
|
||||
error = '';
|
||||
submitting = true;
|
||||
try {
|
||||
await apiRequest(`/game/${state.game.id}/player/${state.player.id}/set-name`, 'POST', {
|
||||
new_name: newNameInput.trim()
|
||||
});
|
||||
newNameInput = '';
|
||||
} catch (e) {
|
||||
error = e.message;
|
||||
} finally {
|
||||
submitting = false;
|
||||
}
|
||||
}
|
||||
|
||||
function onKeydown(e) {
|
||||
if (e.key === 'Enter') submitNewName();
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if state.player.needs_name}
|
||||
<div class="modal-backdrop">
|
||||
<div class="modal-box glass-panel">
|
||||
<h3>🏴☠️ You Earned a Name!</h3>
|
||||
<p class="info-text">
|
||||
You completed your second objective and ranked up. No more going by your smell — what is your new Pirate Name?
|
||||
</p>
|
||||
{#if error}
|
||||
<div class="alert alert-danger">{error}</div>
|
||||
{/if}
|
||||
<input
|
||||
type="text"
|
||||
bind:value={newNameInput}
|
||||
class="input-field"
|
||||
placeholder="Enter your Pirate Name..."
|
||||
on:keydown={onKeydown}
|
||||
autofocus
|
||||
>
|
||||
<button
|
||||
class="btn btn-gold btn-full"
|
||||
on:click={submitNewName}
|
||||
disabled={!newNameInput.trim() || submitting}
|
||||
>
|
||||
Claim Name
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.modal-backdrop {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 2000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20px;
|
||||
background: color-mix(in srgb, var(--bg-deep) 70%, transparent);
|
||||
backdrop-filter: blur(6px);
|
||||
}
|
||||
.modal-box {
|
||||
max-width: 440px;
|
||||
width: 100%;
|
||||
padding: 1.75rem;
|
||||
border: 1px solid var(--edge);
|
||||
border-radius: var(--radius-md);
|
||||
box-shadow: var(--shadow-deep);
|
||||
text-align: center;
|
||||
}
|
||||
.modal-box h3 {
|
||||
margin-top: 0;
|
||||
}
|
||||
.modal-box .input-field {
|
||||
width: 100%;
|
||||
margin: 0.75rem 0;
|
||||
}
|
||||
</style>
|
||||
@@ -5,7 +5,6 @@
|
||||
export let state;
|
||||
|
||||
let error = '';
|
||||
let newNameInput = '';
|
||||
let pvpOpponentId = '';
|
||||
let pvpCard = '';
|
||||
|
||||
@@ -21,19 +20,6 @@
|
||||
// What the chosen duel card becomes as a temporary Obstacle for the defender
|
||||
$: pvpCardObstacle = cardObstacleInfo(pvpCard, $obstacleTable);
|
||||
|
||||
async function submitNewName() {
|
||||
if (!newNameInput.trim()) return;
|
||||
error = '';
|
||||
try {
|
||||
await apiRequest(`/game/${state.game.id}/player/${state.player.id}/set-name`, 'POST', {
|
||||
new_name: newNameInput.trim()
|
||||
});
|
||||
newNameInput = '';
|
||||
} catch(e) {
|
||||
error = e.message;
|
||||
}
|
||||
}
|
||||
|
||||
async function createPvp() {
|
||||
error = '';
|
||||
try {
|
||||
@@ -83,17 +69,6 @@
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if state.player.needs_name}
|
||||
<div class="prompt-box accent">
|
||||
<h4>🏴☠️ You Earned a Name!</h4>
|
||||
<p class="info-text" style="margin-bottom: 0.5rem;">You completed your second objective and ranked up! What is your new Pirate Name?</p>
|
||||
<div class="input-row">
|
||||
<input type="text" bind:value={newNameInput} class="input-field" placeholder="Enter new name...">
|
||||
<button class="btn btn-gold" on:click={submitNewName} disabled={!newNameInput.trim()}>Claim Name</button>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if state.player.tax_banned}
|
||||
<div class="prompt-box danger text-center">
|
||||
<p class="info-text" style="margin: 0; font-size: 0.9rem;">🚫 You failed a refused Tax — no more Gat/Name Taxes for you this scene.</p>
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
import RecruitPhase from '../components/RecruitPhase.svelte';
|
||||
import GameOverPhase from '../components/GameOverPhase.svelte';
|
||||
import EventLog from '../components/EventLog.svelte';
|
||||
import NameModal from '../components/NameModal.svelte';
|
||||
|
||||
export let params = {};
|
||||
let gameId = params.id;
|
||||
@@ -210,6 +211,7 @@
|
||||
</div>
|
||||
|
||||
<EventLog {state} />
|
||||
<NameModal {state} />
|
||||
{:else if !error}
|
||||
<div class="loading-container">
|
||||
<p>Loading game state...</p>
|
||||
|
||||
Reference in New Issue
Block a user