| 6 | "": "// \\\\u{2500}\\\\u{2500}\\\\u{2500} Config & Constants \\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\nconst CONTRACT_ID = \\\\\\\"qelo.near\\\\\\\";\\\\nconst TOKEN_CONTRACT_ID = \\\\\\\"qelo.launch.intear.near\\\\\\\";\\\\nconst QELO_DECIMALS = 24;\\\\nconst ONE_YOCTO = \\\\\\\"1\\\\\\\";\\\\nconst GAS_FEE = \\\\\\\"100000000000000\\\\\\\"; // 100 TGas biar aman\\\\nconst STORAGE_DEPOSIT_AMOUNT = \\\\\\\"50000000000000000000000\\\\\\\"; // 0.05 NEAR\\\\n\\\\nconst MY_LOGO_URL =\\\\n \\\\\\\"https://ipfs.near.social/ipfs/bafkreibn5fgqqxlz3agpgclswjxm2fgxjyy6fhmrzftlftdmq2sv25k7ly\\\\\\\";\\\\n\\\\n// \\\\u{2500}\\\\u{2500}\\\\u{2500} Context & State \\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\nconst accountId = context.accountId;\\\\n\\\\n// Inisialisasi State Awal\\\\nState.init({\\\\n receiver: \\\\\\\"\\\\\\\",\\\\n text: \\\\\\\"\\\\\\\",\\\\n amount: \\\\\\\"1\\\\\\\",\\\\n activeTab: \\\\\\\"chat\\\\\\\",\\\\n selectedSpaceId: \\\\\\\"\\\\\\\",\\\\n newSpaceName: \\\\\\\"\\\\\\\",\\\\n newSpaceMinQelo: \\\\\\\"10\\\\\\\",\\\\n showCreateSpace: false,\\\\n lastRefresh: Date.now(),\\\\n});\\\\n\\\\nif (!state.timerStarted) {\\\\n State.update({ timerStarted: true });\\\\n setInterval(() => {\\\\n State.update({ lastRefresh: Date.now() });\\\\n }, 5000);\\\\n}\\\\n\\\\n// \\\\u{2500}\\\\u{2500}\\\\u{2500} Fetch Data (Auto-Refresh by BOS) \\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\nconst globalFeed =\\\\n Near.view(CONTRACT_ID, \\\\\\\"get_global_feed\\\\\\\", { limit: 100 }) || [];\\\\nconst allSpaces = Near.view(CONTRACT_ID, \\\\\\\"get_spaces\\\\\\\", {}) || [];\\\\n\\\\nlet qeloBalance = \\\\\\\"0\\\\\\\";\\\\nif (accountId) {\\\\n qeloBalance =\\\\n Near.view(TOKEN_CONTRACT_ID, \\\\\\\"ft_balance_of\\\\\\\", { account_id: accountId }) ||\\\\n \\\\\\\"0\\\\\\\";\\\\n}\\\\n\\\\n// \\\\u{2500}\\\\u{2500}\\\\u{2500} Helpers \\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\nconst formatQelo = (yocto) => {\\\\n if (!yocto || yocto === \\\\\\\"0\\\\\\\") return \\\\\\\"0\\\\\\\";\\\\n const str = yocto.toString().padStart(QELO_DECIMALS + 1, \\\\\\\"0\\\\\\\");\\\\n const intPart = str.slice(0, str.length - QELO_DECIMALS) || \\\\\\\"0\\\\\\\";\\\\n const decPart = str.slice(\\\\n str.length - QELO_DECIMALS,\\\\n str.length - QELO_DECIMALS + 2\\\\n );\\\\n return `${Number(intPart).toLocaleString()}.${decPart}`;\\\\n};\\\\n\\\\nconst multiplyQelo = (human) => {\\\\n const parts = human.toString().split(\\\\\\\".\\\\\\\");\\\\n const intPart = parts[0] || \\\\\\\"0\\\\\\\";\\\\n const decPart = (parts[1] || \\\\\\\"\\\\\\\")\\\\n .padEnd(QELO_DECIMALS, \\\\\\\"0\\\\\\\")\\\\n .slice(0, QELO_DECIMALS);\\\\n return (intPart + decPart).replace(/^0+/, \\\\\\\"\\\\\\\") || \\\\\\\"0\\\\\\\";\\\\n};\\\\n\\\\n// \\\\u{2500}\\\\u{2500}\\\\u{2500} Styling (The \\\\\\\"Wah\\\\\\\" Factor) \\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\nconst MainContainer = styled.div`\\\\n --bg-primary: #050505;\\\\n --neon-mint: #00EC97;\\\\n --neon-mint-dim: rgba(0, 236, 151, 0.2);\\\\n --text-primary: #FFFFFF;\\\\n --text-muted: #888888;\\\\n --bg-card: rgba(15, 15, 15, 0.8);\\\\n --border-color: #1a1a1a;\\\\n\\\\n background: radial-gradient(circle at 50% 0%, #112a20 0%, var(--bg-primary) 50%);\\\\n color: var(--text-primary);\\\\n min-height: 100vh;\\\\n padding: 2rem;\\\\n font-family: \\\\\\'Inter\\\\\\', system-ui, sans-serif;\\\\n\\\\n .glass-panel { \\\\n background: var(--bg-card); \\\\n backdrop-filter: blur(10px);\\\\n border: 1px solid var(--border-color); \\\\n border-radius: 1rem; \\\\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);\\\\n }\\\\n\\\\n .gradient-text { \\\\n background: linear-gradient(90deg, #FFFFFF, var(--neon-mint));\\\\n -webkit-background-clip: text;\\\\n -webkit-text-fill-color: transparent;\\\\n font-weight: 800; \\\\n }\\\\n \\\\n .nav-tab {\\\\n padding: 12px 24px; cursor: pointer; border-bottom: 2px solid transparent;\\\\n color: var(--text-muted); transition: all 0.3s ease; font-weight: 500;\\\\n }\\\\n .nav-tab:hover { color: #fff; }\\\\n .nav-tab.active { border-color: var(--neon-mint); color: var(--neon-mint); text-shadow: 0 0 10px var(--neon-mint-dim); }\\\\n\\\\n input, textarea { \\\\n background: rgba(0,0,0,0.5); border: 1px solid #333; color: white; \\\\n padding: 0.8rem; border-radius: 0.5rem; width: 100%; margin-top: 0.5rem; transition: border 0.3s;\\\\n }\\\\n input:focus, textarea:focus { outline: none; border-color: var(--neon-mint); }\\\\n\\\\n button.primary { \\\\n background: var(--neon-mint); color: #000; font-weight: 800; \\\\n padding: 0.8rem; border-radius: 0.5rem; border: none; width: 100%; \\\\n cursor: pointer; transition: transform 0.2s, box-shadow 0.2s;\\\\n }\\\\n button.primary:hover { transform: translateY(-2px); box-shadow: 0 5px 15px var(--neon-mint-dim); }\\\\n \\\\n button.secondary { \\\\n background: transparent; color: var(--neon-mint); border: 1px solid var(--neon-mint); \\\\n padding: 0.5rem 1rem; border-radius: 0.5rem; font-size: 13px; cursor: pointer; transition: all 0.2s;\\\\n }\\\\n button.secondary:hover { background: var(--neon-mint-dim); }\\\\n\\\\n .chat-box {\\\\n max-height: 60vh; overflow-y: auto; padding-right: 10px;\\\\n display: flex; flex-direction: column; gap: 1rem;\\\\n }\\\\n \\\\n /* Custom Scrollbar */\\\\n ::-webkit-scrollbar { width: 6px; }\\\\n ::-webkit-scrollbar-track { background: transparent; }\\\\n ::-webkit-scrollbar-thumb { background: #333; border-radius: 10px; }\\\\n ::-webkit-scrollbar-thumb:hover { background: var(--neon-mint); }\\\\n`;\\\\n\\\\nconst SpaceCard = styled.div`\\\\n background: rgba(255,255,255,0.02);\\\\n border: 1px solid #222; padding: 1rem; border-radius: 0.75rem;\\\\n display: flex; justify-content: space-between; align-items: center;\\\\n transition: all 0.3s;\\\\n &:hover { border-color: var(--neon-mint); transform: translateX(5px); }\\\\n`;\\\\n\\\\nconst MessageBubble = styled.div`\\\\n display: flex; flex-direction: column;\\\\n align-items: ${(props) => (props.isSender ? \\\\\\\"flex-end\\\\\\\" : \\\\\\\"flex-start\\\\\\\")};\\\\n animation: fadeIn 0.3s ease-in;\\\\n\\\\n @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }\\\\n\\\\n .bubble-content {\\\\n max-width: 85%; padding: 12px 18px; border-radius: 1rem;\\\\n background: ${(props) =>\\\\n props.isSender ? \\\\\\\"var(--neon-mint-dim)\\\\\\\" : \\\\\\\"#161616\\\\\\\"};\\\\n border: 1px solid ${(props) =>\\\\n props.isSender ? \\\\\\\"var(--neon-mint)\\\\\\\" : \\\\\\\"#2a2a2a\\\\\\\"};\\\\n border-bottom-right-radius: ${(props) => (props.isSender ? \\\\\\\"4px\\\\\\\" : \\\\\\\"1rem\\\\\\\")};\\\\n border-bottom-left-radius: ${(props) => (!props.isSender ? \\\\\\\"4px\\\\\\\" : \\\\\\\"1rem\\\\\\\")};\\\\n }\\\\n`;\\\\n\\\\n// \\\\u{2500}\\\\u{2500}\\\\u{2500} Actions \\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\nconst handleSend = () => {\\\\n if (!accountId) return;\\\\n const payload = {\\\\n receiver: state.receiver || \\\\\\\"qelo.near\\\\\\\", // Default receiver if empty\\\\n text: state.text,\\\\n space_id: state.selectedSpaceId ? parseInt(state.selectedSpaceId) : null,\\\\n };\\\\n\\\\n Near.call(\\\\n TOKEN_CONTRACT_ID,\\\\n \\\\\\\"ft_transfer_call\\\\\\\",\\\\n {\\\\n receiver_id: CONTRACT_ID,\\\\n amount: multiplyQelo(state.amount),\\\\n msg: JSON.stringify(payload),\\\\n },\\\\n GAS_FEE,\\\\n ONE_YOCTO\\\\n );\\\\n};\\\\n\\\\nconst handleRegisterStorage = () => {\\\\n Near.call(\\\\n CONTRACT_ID,\\\\n \\\\\\\"storage_deposit\\\\\\\",\\\\n {},\\\\n GAS_FEE,\\\\n STORAGE_DEPOSIT_AMOUNT\\\\n );\\\\n};\\\\n\\\\n// \\\\u{2500}\\\\u{2500}\\\\u{2500} Render \\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\u{2500}\\\\nreturn (\\\\n <MainContainer>\\\\n {/* Header */}\\\\n <div className=\\\\\\\"glass-panel p-4 mb-5 d-flex justify-content-between align-items-center\\\\\\\">\\\\n <div className=\\\\\\\"d-flex align-items-center gap-3\\\\\\\">\\\\n <img\\\\n src={MY_LOGO_URL}\\\\n style={{\\\\n width: \\\\\\\"48px\\\\\\\",\\\\n borderRadius: \\\\\\\"50%\\\\\\\",\\\\n border: \\\\\\\"2px solid var(--neon-mint)\\\\\\\",\\\\n padding: \\\\\\\"2px\\\\\\\",\\\\n }}\\\\n />\\\\n <div>\\\\n <h1 className=\\\\\\\"gradient-text m-0 text-2xl\\\\\\\">QeloSuperApp</h1>\\\\n <div\\\\n style={{\\\\n fontSize: \\\\\\\"12px\\\\\\\",\\\\n color: \\\\\\\"var(--neon-mint)\\\\\\\",\\\\n letterSpacing: \\\\\\\"1px\\\\\\\",\\\\n }}\\\\n >\\\\n HUB v2 \\\\u{2022} DECENTRALIZED\\\\n </div>\\\\n </div>\\\\n </div>\\\\n <div className=\\\\\\\"text-right\\\\\\\">\\\\n <div style={{ color: \\\\\\\"var(--text-primary)\\\\\\\", fontSize: \\\\\\\"14px\\\\\\\" }}>\\\\n Wallet:{\\\\\\\" \\\\\\\"}\\\\n <span style={{ color: \\\\\\\"var(--neon-mint)\\\\\\\", fontWeight: \\\\\\\"bold\\\\\\\" }}>\\\\n {accountId || \\\\\\\"Not Connected\\\\\\\"}\\\\n </span>\\\\n </div>\\\\n <div\\\\n style={{\\\\n color: \\\\\\\"var(--text-muted)\\\\\\\",\\\\n fontSize: \\\\\\\"12px\\\\\\\",\\\\n marginTop: \\\\\\\"4px\\\\\\\",\\\\n }}\\\\n >\\\\n Balance: {formatQelo(qeloBalance)} $QELO\\\\n </div>\\\\n </div>\\\\n </div>\\\\n\\\\n {/* Main Grid */}\\\\n <div className=\\\\\\\"row g-4\\\\\\\">\\\\n {/* Kolom Kiri: Navigasi & Feed */}\\\\n <div className=\\\\\\\"col-lg-8\\\\\\\">\\\\n <div className=\\\\\\\"glass-panel p-0 overflow-hidden\\\\\\\">\\\\n {/* Tabs */}\\\\n <div\\\\n className=\\\\\\\"d-flex\\\\\\\"\\\\n style={{\\\\n borderBottom: \\\\\\\"1px solid #1a1a1a\\\\\\\",\\\\n background: \\\\\\\"rgba(0,0,0,0.3)\\\\\\\",\\\\n }}\\\\n >\\\\n <div\\\\n className={`nav-tab ${\\\\n state.activeTab === \\\\\\\"chat\\\\\\\" ? \\\\\\\"active\\\\\\\" : \\\\\\\"\\\\\\\"\\\\n }`}\\\\n onClick={() =>\\\\n State.update({ activeTab: \\\\\\\"chat\\\\\\\", selectedSpaceId: \\\\\\\"\\\\\\\" })\\\\n }\\\\n >\\\\n <i className=\\\\\\\"bi bi-chat-square-text\\\\\\\"></i> Global Feed\\\\n </div>\\\\n <div\\\\n className={`nav-tab ${\\\\n state.activeTab === \\\\\\\"spaces\\\\\\\" ? \\\\\\\"active\\\\\\\" : \\\\\\\"\\\\\\\"\\\\n }`}\\\\n onClick={() => State.update({ activeTab: \\\\\\\"spaces\\\\\\\" })}\\\\n >\\\\n <i className=\\\\\\\"bi bi-boxes\\\\\\\"></i> Hub Spaces\\\\n </div>\\\\n </div>\\\\n\\\\n <div className=\\\\\\\"p-4\\\\\\\">\\\\n {state.activeTab === \\\\\\\"chat\\\\\\\" ? (\\\\n <div className=\\\\\\\"chat-box\\\\\\\">\\\\n {globalFeed.length === 0 ? (\\\\n <div className=\\\\\\\"text-center text-muted py-5\\\\\\\">\\\\n No messages yet. Be the first!\\\\n </div>\\\\n ) : (\\\\n globalFeed.map((msg) => (\\\\n <MessageBubble\\\\n isSender={msg.sender === accountId}\\\\n key={msg.id}\\\\n >\\\\n <div\\\\n className=\\\\\\\"mb-1 d-flex gap-2 align-items-center\\\\\\\"\\\\n style={{\\\\n fontSize: \\\\\\\"11px\\\\\\\",\\\\n color: \\\\\\\"#888\\\\\\\",\\\\n padding: \\\\\\\"0 4px\\\\\\\",\\\\n }}\\\\n >\\\\n <span\\\\n style={{\\\\n fontWeight: \\\\\\\"bold\\\\\\\",\\\\n color:\\\\n msg.sender === accountId\\\\n ? \\\\\\\"var(--neon-mint)\\\\\\\"\\\\n : \\\\\\\"#aaa\\\\\\\",\\\\n }}\\\\n >\\\\n @{msg.sender}\\\\n </span>\\\\n <span>\\\\u{2022}</span>\\\\n <span>\\\\n {new Date(msg.timestamp).toLocaleTimeString([], {\\\\n hour: \\\\\\\"2-digit\\\\\\\",\\\\n minute: \\\\\\\"2-digit\\\\\\\",\\\\n })}\\\\n </span>\\\\n {msg.space_id != null && (\\\\n <span\\\\n style={{\\\\n background: \\\\\\\"#222\\\\\\\",\\\\n padding: \\\\\\\"2px 6px\\\\\\\",\\\\n borderRadius: \\\\\\\"4px\\\\\\\",\\\\n border: \\\\\\\"1px solid #444\\\\\\\",\\\\n }}\\\\n >\\\\n Space #{msg.space_id}\\\\n </span>\\\\n )}\\\\n </div>\\\\n <div className=\\\\\\\"bubble-content\\\\\\\">\\\\n <div style={{ fontSize: \\\\\\\"15px\\\\\\\", lineHeight: \\\\\\\"1.4\\\\\\\" }}>\\\\n {msg.text}\\\\n </div>\\\\n <div\\\\n style={{\\\\n color: \\\\\\\"var(--neon-mint)\\\\\\\",\\\\n fontSize: \\\\\\\"10px\\\\\\\",\\\\n marginTop: \\\\\\\"8px\\\\\\\",\\\\n fontWeight: \\\\\\\"bold\\\\\\\",\\\\n letterSpacing: \\\\\\\"0.5px\\\\\\\",\\\\n }}\\\\n >\\\\n <i className=\\\\\\\"bi bi-gem\\\\\\\"></i>{\\\\\\\" \\\\\\\"}\\\\n {formatQelo(msg.token_amount)} QELO\\\\n </div>\\\\n </div>\\\\n </MessageBubble>\\\\n ))\\\\n )}\\\\n </div>\\\\n ) : (\\\\n <div>\\\\n <div className=\\\\\\\"d-flex justify-content-between align-items-center mb-4\\\\\\\">\\\\n <h4 className=\\\\\\\"m-0 text-white\\\\\\\">Explore Spaces</h4>\\\\n <button\\\\n className=\\\\\\\"secondary\\\\\\\"\\\\n onClick={() =>\\\\n State.update({ showCreateSpace: !state.showCreateSpace })\\\\n }\\\\n >\\\\n {state.showCreateSpace ? \\\\\\\"Cancel\\\\\\\" : \\\\\\\"+ Deploy New Space\\\\\\\"}\\\\n </button>\\\\n </div>\\\\n\\\\n {state.showCreateSpace && (\\\\n <div\\\\n className=\\\\\\\"glass-panel p-4 mb-4\\\\\\\"\\\\n style={{\\\\n border: \\\\\\\"1px dashed var(--neon-mint)\\\\\\\",\\\\n background: \\\\\\\"rgba(0, 236, 151, 0.05)\\\\\\\",\\\\n }}\\\\n >\\\\n <h5 className=\\\\\\\"mb-3 text-white\\\\\\\">Deploy a Smart Space</h5>\\\\n <input\\\\n placeholder=\\\\\\\"Space Name (e.g., Crypto Indo)\\\\\\\"\\\\n value={state.newSpaceName}\\\\n onChange={(e) =>\\\\n State.update({ newSpaceName: e.target.value })\\\\n }\\\\n />\\\\n <input\\\\n type=\\\\\\\"number\\\\\\\"\\\\n placeholder=\\\\\\\"Minimum $QELO to Join\\\\\\\"\\\\n value={state.newSpaceMinQelo}\\\\n onChange={(e) =>\\\\n State.update({ newSpaceMinQelo: e.target.value })\\\\n }\\\\n />\\\\n <button\\\\n className=\\\\\\\"primary mt-3\\\\\\\"\\\\n onClick={() =>\\\\n Near.call(CONTRACT_ID, \\\\\\\"create_space\\\\\\\", {\\\\n name: state.newSpaceName,\\\\n min_qelo: multiplyQelo(state.newSpaceMinQelo),\\\\n })\\\\n }\\\\n >\\\\n Deploy On-Chain\\\\n </button>\\\\n </div>\\\\n )}\\\\n\\\\n <div className=\\\\\\\"d-flex flex-column gap-2\\\\\\\">\\\\n {allSpaces.length === 0 ? (\\\\n <div className=\\\\\\\"text-center text-muted\\\\\\\">\\\\n No spaces deployed yet.\\\\n </div>\\\\n ) : (\\\\n allSpaces.map((s) => (\\\\n <SpaceCard key={s.id}>\\\\n <div>\\\\n <div\\\\n style={{\\\\n fontWeight: \\\\\\\"800\\\\\\\",\\\\n color: \\\\\\\"#fff\\\\\\\",\\\\n fontSize: \\\\\\\"16px\\\\\\\",\\\\n }}\\\\n >\\\\n {s.name}{\\\\\\\" \\\\\\\"}\\\\n <span style={{ fontSize: \\\\\\\"10px\\\\\\\", color: \\\\\\\"#666\\\\\\\" }}>\\\\n ID: #{s.id}\\\\n </span>\\\\n </div>\\\\n <div\\\\n style={{\\\\n fontSize: \\\\\\\"12px\\\\\\\",\\\\n color: \\\\\\\"var(--neon-mint)\\\\\\\",\\\\n marginTop: \\\\\\\"4px\\\\\\\",\\\\n }}\\\\n >\\\\n Min Hold: {formatQelo(s.min_qelo)} QELO\\\\n </div>\\\\n <div\\\\n style={{\\\\n fontSize: \\\\\\\"10px\\\\\\\",\\\\n color: \\\\\\\"#666\\\\\\\",\\\\n marginTop: \\\\\\\"2px\\\\\\\",\\\\n }}\\\\n >\\\\n Owner: {s.owner}\\\\n </div>\\\\n </div>\\\\n <div className=\\\\\\\"d-flex gap-2\\\\\\\">\\\\n <button\\\\n className=\\\\\\\"secondary\\\\\\\"\\\\n onClick={() =>\\\\n Near.call(CONTRACT_ID, \\\\\\\"join_space\\\\\\\", {\\\\n space_id: s.id,\\\\n })\\\\n }\\\\n >\\\\n Join\\\\n </button>\\\\n <button\\\\n className=\\\\\\\"secondary\\\\\\\"\\\\n style={{ borderColor: \\\\\\\"#fff\\\\\\\", color: \\\\\\\"#fff\\\\\\\" }}\\\\n onClick={() =>\\\\n State.update({\\\\n activeTab: \\\\\\\"chat\\\\\\\",\\\\n selectedSpaceId: s.id.toString(),\\\\n })\\\\n }\\\\n >\\\\n Chat Here\\\\n </button>\\\\n </div>\\\\n </SpaceCard>\\\\n ))\\\\n )}\\\\n </div>\\\\n </div>\\\\n )}\\\\n </div>\\\\n </div>\\\\n </div>\\\\n\\\\n {/* Kolom Kanan: Form Kirim & Profil */}\\\\n <div className=\\\\\\\"col-lg-4\\\\\\\">\\\\n <div className=\\\\\\\"glass-panel p-4 mb-4\\\\\\\">\\\\n <h4 className=\\\\\\\"m-0 mb-4 text-white\\\\\\\">Broadcast Message</h4>\\\\n\\\\n {!accountId ? (\\\\n <div className=\\\\\\\"text-center p-4 border border-secondary rounded\\\\\\\">\\\\n <p className=\\\\\\\"text-muted\\\\\\\">\\\\n Connect your NEAR wallet to participate.\\\\n </p>\\\\n </div>\\\\n ) : (\\\\n <div className=\\\\\\\"d-flex flex-column gap-3\\\\\\\">\\\\n <div>\\\\n <label style={{ fontSize: \\\\\\\"12px\\\\\\\", color: \\\\\\\"var(--neon-mint)\\\\\\\" }}>\\\\n Receiver (Optional)\\\\n </label>\\\\n <input\\\\n value={state.receiver}\\\\n onChange={(e) => State.update({ receiver: e.target.value })}\\\\n placeholder=\\\\\\\"Leave empty for public\\\\\\\"\\\\n />\\\\n </div>\\\\n\\\\n <div>\\\\n <label style={{ fontSize: \\\\\\\"12px\\\\\\\", color: \\\\\\\"var(--neon-mint)\\\\\\\" }}>\\\\n Space ID (Optional)\\\\n </label>\\\\n <input\\\\n type=\\\\\\\"number\\\\\\\"\\\\n value={state.selectedSpaceId}\\\\n onChange={(e) =>\\\\n State.update({ selectedSpaceId: e.target.value })\\\\n }\\\\n placeholder=\\\\\\\"Target specific space\\\\\\\"\\\\n />\\\\n </div>\\\\n\\\\n <div>\\\\n <label style={{ fontSize: \\\\\\\"12px\\\\\\\", color: \\\\\\\"var(--neon-mint)\\\\\\\" }}>\\\\n Amount ($QELO)\\\\n </label>\\\\n <input\\\\n type=\\\\\\\"number\\\\\\\"\\\\n value={state.amount}\\\\n onChange={(e) => State.update({ amount: e.target.value })}\\\\n min=\\\\\\\"1\\\\\\\"\\\\n />\\\\n </div>\\\\n\\\\n <div>\\\\n <label style={{ fontSize: \\\\\\\"12px\\\\\\\", color: \\\\\\\"var(--neon-mint)\\\\\\\" }}>\\\\n Message\\\\n </label>\\\\n <textarea\\\\n rows=\\\\\\\"3\\\\\\\"\\\\n value={state.text}\\\\n onChange={(e) => State.update({ text: e.target.value })}\\\\n placeholder=\\\\\\\"What\\\\\\'s on your mind?\\\\\\\"\\\\n />\\\\n </div>\\\\n\\\\n <button className=\\\\\\\"primary mt-2\\\\\\\" onClick={handleSend}>\\\\n <i className=\\\\\\\"bi bi-send-fill me-2\\\\\\\"></i> Send On-Chain\\\\n </button>\\\\n </div>\\\\n )}\\\\n </div>\\\\n\\\\n {/* Info Box untuk Storage */}\\\\n <div\\\\n className=\\\\\\\"glass-panel p-4\\\\\\\"\\\\n style={{ background: \\\\\\\"rgba(255,255,255,0.02)\\\\\\\" }}\\\\n >\\\\n <h6 className=\\\\\\\"text-white mb-2\\\\\\\">\\\\n <i className=\\\\\\\"bi bi-info-circle\\\\\\\"></i> First Time User?\\\\n </h6>\\\\n <p style={{ fontSize: \\\\\\\"12px\\\\\\\", color: \\\\\\\"#888\\\\\\\" }}>\\\\n To save your messages on the blockchain, you need to register\\\\n storage once.\\\\n </p>\\\\n <button className=\\\\\\\"secondary w-100\\\\\\\" onClick={handleRegisterStorage}>\\\\n Register Storage (0.05 NEAR)\\\\n </button>\\\\n </div>\\\\n </div>\\\\n </div>\\\\n </MainContainer>\\\\n);\\\\n", |