Skip to main content

Overview

The Seva (service) submission system allows community members to report their service activities. The platform tracks volunteer hours, meals served, community events, and fundraising efforts, aggregating data for impact reporting.

Seva Types

The platform supports two types of seva submissions:
Track community service activities including:
  • Volunteer hours
  • Meals served
  • Community events organized
  • Funds raised for causes
Route: /community-seva/submit-data

Community Seva Form

The community seva submission form collects quantifiable service metrics:
app/community-seva/submit-data/page.tsx
const FormSchema = z.object({
  submission_by: z.string().min(1, "Data Submitter is required"),
  event_name: z.string().min(1, "Event Name is required"),
  volunteer_hours: z.string()
    .min(1, "Total Volunteer Hours is required")
    .refine((val) => {
      const num = parseInt(val)
      return !isNaN(num) && num >= 0
    }, "Must be a valid number"),
  meals_served: z.string()
    .min(1, "# Meals Served is required")
    .refine((val) => {
      const num = parseInt(val)
      return !isNaN(num) && num >= 0
    }, "Must be a valid number"),
  community_events: z.string()
    .min(1, "# of Community events is required")
    .refine((val) => {
      const num = parseInt(val)
      return !isNaN(num) && num >= 0
    }, "Must be a valid number"),
  funds_raised: z.string()
    .min(1, "$ Funds Raised is required")
    .refine((val) => {
      const num = parseFloat(val)
      return !isNaN(num) && num >= 0
    }, "Must be a valid number"),
})

type FormData = z.infer<typeof FormSchema>

Form Implementation

app/community-seva/submit-data/page.tsx
export default function CommunitySevaSubmitPage() {
  const { toast } = useToast()
  const [isSubmitting, setIsSubmitting] = useState(false)
  const [mounted, setMounted] = useState(false)

  const {
    control,
    handleSubmit,
    formState: { errors },
    reset
  } = useForm<FormData>({
    resolver: zodResolver(FormSchema),
    mode: "onBlur",
    defaultValues: {
      submission_by: "",
      event_name: "",
      volunteer_hours: "",
      meals_served: "",
      community_events: "",
      funds_raised: ""
    }
  })

  const onSubmit = async (data: FormData) => {
    setIsSubmitting(true)
    
    try {
      const dbData = {
        submission_by: data.submission_by,
        event_name: data.event_name,
        volunteer_hours: parseInt(data.volunteer_hours),
        meals_served: parseInt(data.meals_served),
        community_events: parseInt(data.community_events),
        funds_raised: parseFloat(data.funds_raised)
      }
      
      const { error } = await supabase
        .from('community_seva_records')
        .insert([dbData])
      
      if (!error) {
        toast({
          title: "Data submitted successfully!",
          description: "Jay Shree Swaminarayan",
          className: "bg-green-500 text-white border-green-400 shadow-xl font-medium",
        })
        reset()
      } else {
        toast({
          title: "Submission failed",
          description: error.message,
          className: "bg-red-500 text-white border-red-400 shadow-xl font-medium",
        })
      }
    } catch (error) {
      toast({
        title: "Submission failed",
        description: "Please check your connection and try again.",
        className: "bg-red-500 text-white border-red-400 shadow-xl font-medium",
      })
    } finally {
      setTimeout(() => setIsSubmitting(false), 2000)
    }
  }

  return (
    <>
      <div className="min-h-[calc(100vh+200px)] w-full reg-page-bg page-bg-extend" data-page="registration">
        <div className="relative z-10 max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 page-bottom-spacing">
          <div className="text-center page-header-spacing">
            <motion.h1 
              className="standard-page-title reg-title"
              initial={{ opacity: 0, y: 30 }}
              animate={{ opacity: 1, y: 0 }}
              transition={{ duration: 0.8, ease: "easeOut" }}
            >
              Community Seva Data Submission
            </motion.h1>
            
            <motion.div 
              className="flex justify-center px-4 mt-8"
              initial={{ opacity: 0, y: 20 }}
              animate={{ opacity: 1, y: 0 }}
              transition={{ duration: 0.8, delay: 0.2, ease: "easeOut" }}
            >
              <p className="text-center text-base md:text-lg lg:text-xl text-gray-600 leading-relaxed max-w-5xl">
                Submit community seva event data and track our collective impact.
              </p>
            </motion.div>
          </div>

          <div className="max-w-3xl mx-auto">
            <motion.div 
              className="relative mt-8"
              initial={{ opacity: 0, y: 50 }}
              animate={{ opacity: 1, y: 0 }}
              transition={{ duration: 0.8, delay: 0.4, ease: "easeOut" }}
            >
              <div className="absolute -inset-4 bg-gradient-to-r from-orange-200/30 via-white/20 to-red-200/30 rounded-[2rem] blur-xl opacity-40 will-change-transform"></div>
              <div className="relative">
                <Card className="reg-card rounded-3xl overflow-hidden relative">
                  <CardHeader className="text-center pb-6 lg:pb-8">
                    <CardTitle className="text-xl lg:text-2xl font-semibold reg-text-primary">Event Data Form</CardTitle>
                    <CardDescription className="reg-text-secondary text-base">Please fill in the event details</CardDescription>
                  </CardHeader>
                  
                  <CardContent>
                    <form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
                      {/* Form fields */}
                    </form>
                  </CardContent>
                </Card>
              </div>
            </motion.div>
          </div>
        </div>
      </div>
      <Toaster />
    </>
  )
}

Form Fields

Data Submitter

Name of person submitting the service data

Event Name

Name or description of the service event

Volunteer Hours

Total hours contributed by all volunteers

Meals Served

Number of meals provided to community members

Community Events

Count of community events organized

Funds Raised

Dollar amount raised for charitable causes

Validation Rules

1

Required Fields

All fields are required and cannot be left empty
2

Numeric Validation

Hours, meals, and events must be valid non-negative integers
3

Currency Validation

Funds raised must be a valid non-negative decimal number (supports cents)
4

Inline Feedback

Errors display below each field with reg-error-text styling

Database Schema

CREATE TABLE community_seva_records (
  id SERIAL PRIMARY KEY,
  submission_by TEXT NOT NULL,
  event_name TEXT NOT NULL,
  volunteer_hours INTEGER NOT NULL CHECK (volunteer_hours >= 0),
  meals_served INTEGER NOT NULL CHECK (meals_served >= 0),
  community_events INTEGER NOT NULL CHECK (community_events >= 0),
  funds_raised DECIMAL(10,2) NOT NULL CHECK (funds_raised >= 0),
  created_at TIMESTAMP DEFAULT NOW()
);

Data Type Conversion

The form converts string inputs to appropriate numeric types:
app/community-seva/submit-data/page.tsx
const dbData = {
  submission_by: data.submission_by,
  event_name: data.event_name,
  volunteer_hours: parseInt(data.volunteer_hours),     // String → Integer
  meals_served: parseInt(data.meals_served),           // String → Integer
  community_events: parseInt(data.community_events),   // String → Integer
  funds_raised: parseFloat(data.funds_raised)          // String → Float
}
Number inputs are validated as strings in Zod schema, then converted to proper numeric types before database insertion to ensure accurate validation messages.

Form Reset

After successful submission, the form automatically resets:
if (!error) {
  toast({
    title: "Data submitted successfully!",
    description: "Jay Shree Swaminarayan",
    className: "bg-green-500 text-white border-green-400 shadow-xl font-medium",
  })
  reset() // Reset form to default values
}

Submit Button States

app/community-seva/submit-data/page.tsx
<button 
  type="submit" 
  disabled={isSubmitting}
  className="reg-button relative w-full h-14 inline-flex items-center justify-center text-center px-4 py-2 text-base rounded-lg overflow-hidden"
>
  <div className={`absolute inset-0 bg-gradient-to-b from-white/20 to-transparent transform transition-transform duration-500 ${isSubmitting ? 'translate-y-0' : 'translate-y-full'}`}></div>
  <div className="relative z-10 flex items-center justify-center gap-2">
    {isSubmitting ? (
      <>
        <Loader2 className="w-5 h-5 animate-spin" />
        Please wait
      </>
    ) : (
      <>
        <Send className="w-5 h-5" />
        Submit Data
      </>
    )}
  </div>
</button>

Error Handling

Inline validation errors appear below each field when blur occurs:
{errors.volunteer_hours && (
  <p className="reg-error-text">{errors.volunteer_hours.message}</p>
)}

Reporting & Analytics

Submitted data can be aggregated for impact reports:
-- Total community impact metrics
SELECT 
  SUM(volunteer_hours) as total_hours,
  SUM(meals_served) as total_meals,
  SUM(community_events) as total_events,
  SUM(funds_raised) as total_funds
FROM community_seva_records;

-- Impact by event
SELECT 
  event_name,
  SUM(volunteer_hours) as hours,
  SUM(meals_served) as meals
FROM community_seva_records
GROUP BY event_name
ORDER BY hours DESC;

Styling

The seva forms reuse registration theme styles:
  • reg-page-bg - Background gradient
  • reg-card - Form card container
  • reg-input - Input field styling
  • reg-button - Submit button with animation
  • reg-label - Form label styling
  • reg-error-text - Error message styling
The form uses mode: "onBlur" validation, meaning errors only appear after users leave a field. This prevents intrusive validation during typing.

Build docs developers (and LLMs) love