From 5ea9cec8dcf0204006a5cee9fa61d609965c77d2 Mon Sep 17 00:00:00 2001 From: Benedict Balogun <50557035+wolfyres@users.noreply.github.com> Date: Sat, 27 Jun 2026 14:47:19 +0100 Subject: [PATCH 1/2] Refactor FAQ data structure and add interfaces --- client/src/pages/FAQ/FAQContent.ts | 109 +++++++++++++++++++++++++---- 1 file changed, 95 insertions(+), 14 deletions(-) diff --git a/client/src/pages/FAQ/FAQContent.ts b/client/src/pages/FAQ/FAQContent.ts index 0f9b4359..ef36cfa1 100644 --- a/client/src/pages/FAQ/FAQContent.ts +++ b/client/src/pages/FAQ/FAQContent.ts @@ -1,22 +1,103 @@ -export const faqData = [ +export interface FAQItem { + question: string; + answer: string; +} + +export interface FAQCategory { + title: string; + items: FAQItem[]; +} + +export const faqData: FAQData[] = [ { - id: "how-to-buy", - question: "How do I buy a ticket?", - answer: "To buy a ticket, connect your Stellar-compatible wallet (like Albedo or Rabbit), select the lottery you wish to enter, and click 'Purchase'. You will need a small amount of XLM for the transaction fee." + title: "Wallets", + items: [ + { + id: "wallet-support", + question: "Which wallets are supported by the platform?", + answer: "We support major Stellar-compatible wallets, including Freighter, xBull, and Albedo. We highly recommend using Freighter for the smoothest experience on Soroban smart contract networks." + }, + { + question: "How do I connect my Freighter wallet?", + answer: "Click the 'Connect Wallet' button in the top right navigation bar, select 'Freighter' from the modal options, and approve the connection request popup inside your Freighter extension window." + }, + { + question: "Why is my wallet showing the wrong network?", + answer: "Ensure your wallet extension is toggled to the network matching our platform (e.g., Testnet for testing or Mainnet for live draws). You can switch networks in your wallet extension's settings panel." + } + ] }, { - id: "how-to-create", - question: "How do I create a lottery?", - answer: "Navigate to the 'Create' dashboard. Fill in the prize pool details, ticket price, and end date. Once submitted, you will sign a transaction to deploy the Soroban smart contract." + title: "Tickets & Raffles", + items: [ + { + id: "how-to-buy", + question: "How do I buy a ticket?", + answer: "To buy a ticket, connect your Stellar-compatible wallet (like Albedo or Rabbit), select the lottery you wish to enter, and click 'Purchase'. You will need a small amount of XLM for the transaction fee." + }, + { + question: "Can I buy multiple tickets for a single raffle?", + answer: "Yes! There is no limit on how many tickets a single wallet address can purchase, provided the raffle's max ticket pool capacity hasn't been hit. More tickets grant a proportionally higher mathematical probability of winning." + }, + { + question: "What is the refund policy if a raffle is cancelled?", + answer: "If a raffle is cancelled or fails to reach its minimum threshold criteria before the expiration deadline, the Soroban smart contract enters a refundable state. You can claim a 100% refund of your ticket purchase cost directly through the user interface." + }, + { + question: "Are my tickets transferable after purchase?", + answer: "No. Raffle tickets are bound directly to the minting/purchasing wallet address on-chain and cannot be traded or transferred to another account." + } + ] }, { - id: "what-is-vrf", - question: "What is VRF?", - answer: "VRF stands for Verifiable Random Function. It is a cryptographic primitive used to ensure that the winner selection process is 100% fair, transparent, and cannot be manipulated by the developers or other users." + title: "Randomness & Oracle Mechanisms", + items: [ + { + question: "How is the winner chosen?", + answer: "Winners are drawn using an automated execution triggered on the Soroban smart contract. The system pulls an external entropy payload generated by a secure decentralized oracle framework to dictate the winning ticket index impartially." + }, + { + question: "What is a VRF proof?", + answer: "A Verifiable Random Function (VRF) proof is a cryptographic proof that demonstrates a random number was generated purely from a secret seed and public key parameters. It guarantees the output is mathematically unpredictable, tamper-proof, and fully verifiable by anyone on-chain." + }, + { + question: "Can developers or creators rig the outcome?", + answer: "No. Because the calculation runs entirely within an open-source Soroban smart contract coupled with verifiable VRF oracles, neither platform operators nor raffle creators can manipulate the generation sequence." + } + ] }, { - id: "wallet-support", - question: "Which wallets are supported?", - answer: "Currently, we support Albedo, Freighter, and any wallet compatible with the Stellar wallet-connect standard." + title: "Fees & Network Gas", + items: [ + { + question: "What are the transaction fees on the platform?", + answer: "Every ticket purchase and interaction relies on the Stellar network's native fee structure. Soroban smart contracts require execution fees ('resource fees') paid in XLM. These are highly scalable and typically cost a fraction of a cent." + }, + { + question: "Who pays for the oracle random number generation?", + answer: "The platform covers or subsidizes the base operational infrastructure overhead of pulling the oracle feed into the smart contract state, ensuring users only handle their standard ticket purchase interaction gas." + }, + { + question: "What happens if my transaction fails due to low fees?", + answer: "If your wallet sets too low of an XLM base fee during high network utilization spikes, the transaction will time out. Simply bump your max fee parameter in Freighter and retry." + } + ] + }, + { + title: "Transparency & Security", + items: [ + { + question: "Where can I verify the draw on-chain?", + answer: "Every single raffle deployment, ticket purchase execution, and winning draw logs an event stream to the ledger. You can verify all steps by inspecting the specific Soroban Contract ID on network block explorers like StellarExpert." + }, + { + question: "Is the raffle contract open-source?", + answer: "Yes, our Soroban Rust smart contracts are completely open-source. Anyone can audit the logic, structural parameters, state machines, and cryptographic verification mechanisms on our official GitHub organization." + }, + { + question: "How long after the countdown does the automated draw happen?", + answer: "Once the deadline timestamp passes, an on-chain execution call is initiated. The process relies on blockchain block confirmation times, usually triggering and resolving inside 5 to 10 seconds." + } + ] } -]; \ No newline at end of file +]; From c41ee082520045eb6c5b868741b1adbd3f6dd081 Mon Sep 17 00:00:00 2001 From: Benedict Balogun <50557035+wolfyres@users.noreply.github.com> Date: Sat, 27 Jun 2026 14:55:49 +0100 Subject: [PATCH 2/2] Refactor FAQPage to enhance schema handling Refactor FAQPage component to use memoized JSON-LD schema and improve structure. --- client/src/pages/FAQ/FAQPage.tsx | 159 ++++++++++++------------------- 1 file changed, 59 insertions(+), 100 deletions(-) diff --git a/client/src/pages/FAQ/FAQPage.tsx b/client/src/pages/FAQ/FAQPage.tsx index 204390bb..75274011 100644 --- a/client/src/pages/FAQ/FAQPage.tsx +++ b/client/src/pages/FAQ/FAQPage.tsx @@ -3,6 +3,8 @@ import { useLocation } from 'react-router-dom'; import { Helmet } from 'react-helmet-async'; import { Search } from 'lucide-react'; import { faqData } from './FAQContent'; +import React, { useMemo } from 'react'; +import { faqContent } from './FAQContent'; const HighlightText = ({ text, highlight }: { text: string; highlight: string }) => { if (!highlight.trim()) { @@ -94,117 +96,74 @@ const FAQItem = ({ ); }; -const FAQPage = () => { - const [searchQuery, setSearchQuery] = useState(''); - const location = useLocation(); - const [openItems, setOpenItems] = useState>(() => { - // Initial state check for hash - const hashId = window.location.hash.replace('#', ''); - return hashId ? { [hashId]: true } : {}; - }); - - // Keep open items in sync if hash changes externally - useEffect(() => { - const hashId = location.hash.replace('#', ''); - if (hashId && !openItems[hashId]) { - setOpenItems((prev) => ({ ...prev, [hashId]: true })); - } - }, [location.hash]); // eslint-disable-line react-hooks/exhaustive-deps - - const filteredFaqs = faqData.filter( - (item) => - item.question.toLowerCase().includes(searchQuery.toLowerCase()) || - item.answer.toLowerCase().includes(searchQuery.toLowerCase()) - ); +export const FAQPage: React.FC = () => { + // Flatten array and format valid structural JSON-LD matching Schema.org expectations + const jsonLdSchema = useMemo(() => { + const mainEntities = faqContent.flatMap((category) => + category.items.map((item) => ({ + "@type": "Question", + "name": item.question, + "acceptedAnswer": { + "@type": "Answer", + "text": item.answer + } + })) + ); - const toggleItem = (id: string) => { - setOpenItems((prev) => { - const isOpening = !prev[id]; - - // Update hash to reflect state - if (isOpening) { - window.history.pushState(null, '', `#${id}`); - } else if (location.hash === `#${id}`) { - window.history.pushState(null, '', window.location.pathname + window.location.search); - } - - return { ...prev, [id]: isOpening }; + return JSON.stringify({ + "@context": "https://schema.org", + "@type": "FAQPage", + "mainEntity": mainEntities }); - }; - - const schemaData = { - "@context": "https://schema.org", - "@type": "FAQPage", - "mainEntity": faqData.map((item) => ({ - "@type": "Question", - "name": item.question, - "acceptedAnswer": { - "@type": "Answer", - "text": item.answer, - }, - })), - }; + }, []); return ( -
- - FAQ | Tikka - - +
+ {/* Dynamic injection of schema structure into the head element */} +