big updates

added read more
added a prompt to skip reading and auto scroll to sign
moved the erase button to the sig pad so easier to access

updated icon on submit button
This commit is contained in:
fISHIE
2026-01-21 13:17:58 +05:00
parent 1f3d3f13e1
commit 37e6b642ae
4 changed files with 80 additions and 39 deletions

View File

@@ -1,4 +1,4 @@
import { useState } from "react";
import { useState, useRef } from "react";
import { usePetition } from "@/hooks/usePetition";
import { useLanguage } from "@/hooks/useLanguage";
import { submitSignature } from "@/lib/api";
@@ -10,6 +10,7 @@ import { AuthorCard } from "@/components/petition/AuthorCard";
import { PetitionBody } from "@/components/petition/PetitionBody";
import { SignatureForm } from "@/components/signature/SignatureForm";
import { TweetModal } from "@/components/TweetModal";
import { PenLine } from "lucide-react";
function getPetitionIdFromUrl(): string | null {
const urlParams = new URLSearchParams(window.location.search);
@@ -23,6 +24,11 @@ function App() {
const { petition, loading, error, refetch } = usePetition(petitionId);
const { language, setLanguage } = useLanguage();
const [showTweetModal, setShowTweetModal] = useState(false);
const signatureFormRef = useRef<HTMLDivElement>(null);
const scrollToSignForm = () => {
signatureFormRef.current?.scrollIntoView({ behavior: "smooth", block: "start" });
};
const handleSubmit = async (data: {
name: string;
@@ -77,6 +83,14 @@ function App() {
<PetitionHeader petition={petition} language={language} />
<button
onClick={scrollToSignForm}
className={`w-full flex items-center justify-center gap-2 bg-blue-600 hover:bg-blue-700 text-white font-medium py-3 px-6 rounded-lg transition-colors ${language === "dv" ? "flex-row-reverse dhivehi" : ""}`}
>
<PenLine className="w-5 h-5" />
{language === "dv" ? "މިހާރު ސޮއި ކުރައްވާ" : "Sign Now"}
</button>
<AuthorCard
author={petition.authorDetails}
language={language}
@@ -88,7 +102,9 @@ function App() {
language={language}
/>
<SignatureForm language={language} onSubmit={handleSubmit} />
<div ref={signatureFormRef}>
<SignatureForm language={language} onSubmit={handleSubmit} />
</div>
<TweetModal
open={showTweetModal}

View File

@@ -1,6 +1,7 @@
import { useMemo } from "react";
import { useMemo, useState } from "react";
import { marked } from "marked";
import DOMPurify from "dompurify";
import { ChevronDown, ChevronUp } from "lucide-react";
import type { Language } from "@/types/petition";
interface PetitionBodyProps {
@@ -9,11 +10,14 @@ interface PetitionBodyProps {
language: Language;
}
const COLLAPSED_HEIGHT = 300;
export function PetitionBody({
bodyEng,
bodyDhiv,
language,
}: PetitionBodyProps) {
const [isExpanded, setIsExpanded] = useState(false);
const content = language === "dv" ? bodyDhiv : bodyEng;
const isRtl = language === "dv";
@@ -24,21 +28,46 @@ export function PetitionBody({
return (
<div className="mt-8">
<div
className={`leading-loose prose prose-slate max-w-none text-slate-800 md:prose-lg
prose-p:mb-6 prose-p:mt-0
prose-headings:text-slate-900 prose-headings:font-bold prose-headings:mb-6 prose-headings:mt-12
[&_hr]:my-8! [&_hr]:border-slate-300!
[&_ul]:list-disc! [&_ul]:my-6!
[&_ol]:list-decimal! [&_ol]:my-6!
[&_li]:my-2!
[&_li::before]:content-none!
${isRtl ? "dhivehi dir-rtl text-right [&_ul]:pr-12! [&_ol]:pr-12!" : "text-left [&_ul]:pl-12! [&_ol]:pl-12!"}`}
dangerouslySetInnerHTML={{ __html: htmlContent }}
/>
<div className="relative">
<div
className={`leading-loose prose prose-slate max-w-none text-slate-800 md:prose-lg
prose-p:mb-6 prose-p:mt-0
prose-headings:text-slate-900 prose-headings:font-bold prose-headings:mb-6 prose-headings:mt-12
[&_hr]:my-8! [&_hr]:border-slate-300!
[&_ul]:list-disc! [&_ul]:my-6!
[&_ol]:list-decimal! [&_ol]:my-6!
[&_li]:my-2!
[&_li::before]:content-none!
${isRtl ? "dhivehi dir-rtl text-right [&_ul]:pr-12! [&_ol]:pr-12!" : "text-left [&_ul]:pl-12! [&_ol]:pl-12!"}
${!isExpanded ? "overflow-hidden" : ""}`}
style={!isExpanded ? { maxHeight: `${COLLAPSED_HEIGHT}px` } : undefined}
dangerouslySetInnerHTML={{ __html: htmlContent }}
/>
{!isExpanded && (
<div className="absolute bottom-0 left-0 right-0 h-24 bg-gradient-to-t from-white to-transparent pointer-events-none" />
)}
</div>
<button
onClick={() => setIsExpanded(!isExpanded)}
className={`mt-4 flex items-center gap-2 text-blue-600 hover:text-blue-700 font-medium transition-colors ${isRtl ? "flex-row-reverse" : ""}`}
>
{isExpanded ? (
<>
<ChevronUp className="w-5 h-5" />
{language === "dv" ? "ކުޑަކޮށް ދައްކާ" : "Show Less"}
</>
) : (
<>
<ChevronDown className="w-5 h-5" />
{language === "dv" ? "އިތުރަށް ކިޔާ" : "Read More"}
</>
)}
</button>
</div>
);
}

View File

@@ -6,7 +6,7 @@ import { Checkbox } from "@/components/ui/checkbox";
import { Alert, AlertDescription } from "@/components/ui/alert";
import { SignaturePad, type SignaturePadRef } from "./SignaturePad";
import { Turnstile } from "@marsidev/react-turnstile";
import { Eraser, Send, CheckCircle, AlertCircle } from "lucide-react";
import { Eraser, Check, CheckCircle, AlertCircle } from "lucide-react";
import type { Language } from "@/types/petition";
interface SignatureFormProps {
@@ -200,8 +200,18 @@ export function SignatureForm({ language, onSubmit }: SignatureFormProps) {
<Label className={`text-slate-700 ${isRtl ? "dhivehi" : ""}`}>
{language === "en" ? "Signature" : "ސޮއި"}
</Label>
<div className="border border-input rounded-lg overflow-hidden shadow-sm hover:border-ring/50 transition-colors">
<div className="relative border border-input rounded-lg overflow-hidden shadow-sm hover:border-ring/50 transition-colors">
<SignaturePad ref={signaturePadRef} />
<Button
type="button"
variant="ghost"
onClick={handleClear}
size="icon"
className="absolute top-2 right-2 h-8 w-8 text-red-500 hover:text-red-700 hover:bg-red-50/80 bg-white/70 backdrop-blur-sm"
title={language === "en" ? "Clear Signature" : "ފޮހެލާ"}
>
<Eraser className="h-4 w-4" />
</Button>
</div>
<p className={`text-xs text-slate-500 ${isRtl ? "dhivehi" : ""}`}>
{language === "en"
@@ -243,38 +253,25 @@ export function SignatureForm({ language, onSubmit }: SignatureFormProps) {
</Alert>
)}
{/* Turnstile */}
<div className="flex justify-center sm:justify-start">
{/* Turnstile and Submit */}
<div className="flex flex-col items-center sm:items-start gap-4">
<Turnstile
siteKey="0x4AAAAAACHH4QC3wIhkCuhd"
onSuccess={setTurnstileToken}
onError={() => setTurnstileToken(null)}
onExpire={() => setTurnstileToken(null)}
/>
</div>
{/* Form Buttons */}
<div className={`flex gap-4 pt-4 ${isRtl ? "flex-row-reverse" : ""}`}>
<Button
type="button"
variant="ghost"
onClick={handleClear}
className={`text-slate-500 hover:text-slate-900 ${isRtl ? "dhivehi" : ""}`}
>
<Eraser className="h-4 w-4 mr-2" />
{language === "en" ? "Clear Signature" : "ފޮހެލާ"}
</Button>
<Button
type="submit"
disabled={isSubmitting}
size="lg"
className={`min-w-[140px] shadow-md hover:shadow-lg transition-all ${isRtl ? "dhivehi" : ""}`}
className={`w-[300px] bg-green-600 hover:bg-green-700 shadow-md hover:shadow-lg transition-all ${isRtl ? "dhivehi" : ""}`}
>
{isSubmitting ? (
<span className="animate-pulse">Submitting...</span>
) : (
<>
<Send className="h-4 w-4 mr-2" />
<Check className="h-5 w-5 mr-2" />
{language === "en" ? "Submit Petition" : "ހުށަހެޅުއް"}
</>
)}

View File

@@ -60,8 +60,7 @@ export const SignaturePad = forwardRef<SignaturePadRef, SignaturePadProps>(
ref={sigCanvasRef}
penColor="black"
canvasProps={{
className: "w-full",
style: { aspectRatio: "3", height: "auto" },
className: "w-full aspect-[2/1] md:aspect-[3/1]",
}}
onBegin={onBegin}
onEnd={onEnd}