feat: Add profile/edit icons to island markers, fix planets API

- Add profile button to view user profile from island marker
- Add edit button (owner only) to modify island
- Style island action buttons with hover animations
- Fix planets API endpoint to use /planets/nearby
- Fix create planet endpoint to use /planets/create
- Match search bar positioning with map overlay (0.7rem top)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Your Name 2026-01-24 20:46:03 +00:00
parent ef075ccb9b
commit 36626fa091
6 changed files with 99 additions and 31 deletions

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "sociowire-frontend", "name": "sociowire-frontend",
"version": "0.0.107", "version": "0.0.109",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "sociowire-frontend", "name": "sociowire-frontend",
"version": "0.0.107", "version": "0.0.109",
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-free": "^7.1.0", "@fortawesome/fontawesome-free": "^7.1.0",
"axios": "^1.13.2", "axios": "^1.13.2",

View File

@ -1,7 +1,7 @@
{ {
"name": "sociowire-frontend", "name": "sociowire-frontend",
"private": true, "private": true,
"version": "0.0.107", "version": "0.0.109",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite --host 0.0.0.0", "dev": "vite --host 0.0.0.0",

View File

@ -1288,7 +1288,7 @@ export async function fetchPlanets(params = {}) {
if (typeof params.offset === "number") qs.set("offset", String(params.offset)); if (typeof params.offset === "number") qs.set("offset", String(params.offset));
if (params.search) qs.set("search", params.search); if (params.search) qs.set("search", params.search);
const url = `${apiBase()}/planets?${qs.toString()}`; const url = `${apiBase()}/planets/nearby?${qs.toString()}`;
try { try {
const res = await fetch(url, { credentials: "include" }); const res = await fetch(url, { credentials: "include" });
@ -1329,7 +1329,7 @@ export async function fetchPlanet(planetId) {
*/ */
export async function createPlanet(payload) { export async function createPlanet(payload) {
try { try {
const res = await fetch(`${apiBase()}/planets`, { const res = await fetch(`${apiBase()}/planets/create`, {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
headers: { "Content-Type": "application/json", ...authHeaders() }, headers: { "Content-Type": "application/json", ...authHeaders() },

View File

@ -16,24 +16,22 @@
height: 100%; height: 100%;
} }
/* Search bar */ /* Search bar - matches map overlay position */
.islands-world-search { .islands-world-search {
position: fixed; position: absolute;
top: 56px; top: 0.7rem;
left: 50%; left: 50%;
transform: translateX(-50%); transform: translateX(-50%);
z-index: 100; z-index: 100;
display: flex; display: flex;
align-items: center; align-items: center;
gap: 8px; gap: 6px;
background: rgba(15, 23, 42, 0.95); background: rgba(6, 10, 22, 0.86);
backdrop-filter: blur(20px); backdrop-filter: blur(10px);
border: 1px solid rgba(56, 189, 248, 0.3); border: 1px solid rgba(56, 189, 248, 0.45);
border-radius: 50px; border-radius: 14px;
padding: 8px 12px; padding: 6px 10px;
width: auto; max-width: min(580px, calc(100vw - 24px));
max-width: 500px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
} }
.islands-world-search .search-icon { .islands-world-search .search-icon {
@ -166,6 +164,54 @@
margin-top: 2px; margin-top: 2px;
} }
/* Island action buttons */
.island-marker-actions {
position: absolute;
top: 5px;
right: -5px;
display: flex;
flex-direction: column;
gap: 4px;
opacity: 0;
transform: translateX(10px);
transition: all 0.3s ease;
}
.island-marker:hover .island-marker-actions {
opacity: 1;
transform: translateX(0);
}
.island-action-btn {
width: 28px;
height: 28px;
border: none;
border-radius: 50%;
background: rgba(15, 23, 42, 0.9);
border: 1px solid rgba(56, 189, 248, 0.4);
color: rgba(255, 255, 255, 0.9);
font-size: 11px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease;
backdrop-filter: blur(8px);
}
.island-action-btn:hover {
background: rgba(56, 189, 248, 0.3);
transform: scale(1.1);
}
.island-profile-btn:hover {
color: #38bdf8;
}
.island-edit-btn:hover {
color: #10b981;
}
/* Tooltip */ /* Tooltip */
.islands-world-tooltip { .islands-world-tooltip {
position: fixed; position: fixed;

View File

@ -138,12 +138,24 @@ export default function IslandsWorld({ theme, onIslandClick, onOpenMap, onOpenPl
const lng = centerLng + baseX + jitterX; const lng = centerLng + baseX + jitterX;
const lat = centerLat + baseY + jitterY; const lat = centerLat + baseY + jitterY;
const el = createIslandMarker(island, idx, t); const el = createIslandMarker(island, idx, t, username);
el.addEventListener('mouseenter', () => setHoveredIsland(island)); el.addEventListener('mouseenter', () => setHoveredIsland(island));
el.addEventListener('mouseleave', () => setHoveredIsland(null)); el.addEventListener('mouseleave', () => setHoveredIsland(null));
el.addEventListener('click', (e) => { el.addEventListener('click', (e) => {
e.stopPropagation(); e.stopPropagation();
const action = e.target.closest('[data-action]')?.dataset?.action;
if (action === 'profile') {
// Navigate to user profile
window.location.href = `/profile/${island.username}`;
return;
}
if (action === 'edit') {
// Navigate to island edit
window.location.href = `/island/${island.username}/edit`;
return;
}
// Default: open island viewer
onIslandClick?.(island); onIslandClick?.(island);
}); });
@ -251,7 +263,7 @@ export default function IslandsWorld({ theme, onIslandClick, onOpenMap, onOpenPl
/** /**
* Create a stylized island marker element * Create a stylized island marker element
*/ */
function createIslandMarker(island, idx, t) { function createIslandMarker(island, idx, t, currentUsername) {
const el = document.createElement('div'); const el = document.createElement('div');
el.className = 'island-marker'; el.className = 'island-marker';
@ -268,6 +280,8 @@ function createIslandMarker(island, idx, t) {
// Generate unique island shape // Generate unique island shape
const islandSVG = generateIslandSVG(seed, color); const islandSVG = generateIslandSVG(seed, color);
const postCount = island.content_count || 0; const postCount = island.content_count || 0;
const isOwner = currentUsername && island.username &&
currentUsername.toLowerCase() === island.username.toLowerCase();
el.innerHTML = ` el.innerHTML = `
<div class="island-marker-inner" style="animation-delay: ${(idx % 12) * 0.25}s;"> <div class="island-marker-inner" style="animation-delay: ${(idx % 12) * 0.25}s;">
@ -276,6 +290,16 @@ function createIslandMarker(island, idx, t) {
<span class="island-marker-name" style="color: ${color}">@${island.username}</span> <span class="island-marker-name" style="color: ${color}">@${island.username}</span>
<span class="island-marker-meta">${postCount} posts</span> <span class="island-marker-meta">${postCount} posts</span>
</div> </div>
<div class="island-marker-actions">
<button class="island-action-btn island-profile-btn" data-action="profile" title="View Profile">
<i class="fa-solid fa-user"></i>
</button>
${isOwner ? `
<button class="island-action-btn island-edit-btn" data-action="edit" title="Edit Island">
<i class="fa-solid fa-pen"></i>
</button>
` : ''}
</div>
</div> </div>
`; `;

View File

@ -16,24 +16,22 @@
height: 100%; height: 100%;
} }
/* Search bar */ /* Search bar - matches map overlay position */
.planets-world-search { .planets-world-search {
position: fixed; position: absolute;
top: 56px; top: 0.7rem;
left: 50%; left: 50%;
transform: translateX(-50%); transform: translateX(-50%);
z-index: 100; z-index: 100;
display: flex; display: flex;
align-items: center; align-items: center;
gap: 8px; gap: 6px;
background: rgba(15, 15, 26, 0.95); background: rgba(15, 10, 30, 0.86);
backdrop-filter: blur(20px); backdrop-filter: blur(10px);
border: 1px solid rgba(155, 89, 182, 0.3); border: 1px solid rgba(155, 89, 182, 0.45);
border-radius: 50px; border-radius: 14px;
padding: 8px 12px; padding: 6px 10px;
width: auto; max-width: min(580px, calc(100vw - 24px));
max-width: 500px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.6);
} }
.planets-world-search .search-icon { .planets-world-search .search-icon {