Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 

Repository files navigation

CampusShield

import React, { useState } from 'react'; import { AlertCircle, Send, Clock, CheckCircle, Eye, EyeOff, Shield, Users, BookOpen, Heart, AlertTriangle, Search, Filter, Calendar, User } from 'lucide-react';

const StudentReportingPlatform = () => { const [currentView, setCurrentView] = useState('report'); const [reports, setReports] = useState([ { id: 'RPT-001', type: 'harassment', severity: 'high', status: 'investigating', title: 'Inappropriate behavior in dormitory', description: 'Ongoing harassment from senior students in Building A', submittedAt: '2025-07-15', lastUpdated: '2025-07-17', department: 'Student Affairs', anonymous: true }, { id: 'RPT-002', type: 'ragging', severity: 'critical', status: 'escalated', title: 'Ragging incident at mess hall', description: 'First-year students being forced to perform degrading tasks', submittedAt: '2025-07-10', lastUpdated: '2025-07-18', department: 'Disciplinary Committee', anonymous: true }, { id: 'RPT-003', type: 'discrimination', severity: 'medium', status: 'resolved', title: 'Discriminatory remarks in classroom', description: 'Professor making biased comments about certain communities', submittedAt: '2025-07-05', lastUpdated: '2025-07-16', department: 'Academic Affairs', anonymous: false } ]);

const [formData, setFormData] = useState({ type: '', severity: '', title: '', description: '', location: '', datetime: '', witnesses: '', anonymous: true, contactInfo: '' });

const [trackingId, setTrackingId] = useState(''); const [searchFilter, setSearchFilter] = useState({ type: '', status: '', severity: '' });

const issueTypes = [ { value: 'harassment', label: 'Harassment', icon: AlertTriangle, color: 'text-red-600' }, { value: 'ragging', label: 'Ragging/Bullying', icon: Users, color: 'text-red-700' }, { value: 'discrimination', label: 'Discrimination', icon: Users, color: 'text-orange-600' }, { value: 'academic', label: 'Academic Misconduct', icon: BookOpen, color: 'text-blue-600' }, { value: 'safety', label: 'Safety Concerns', icon: Shield, color: 'text-yellow-600' }, { value: 'mental_health', label: 'Mental Health Support', icon: Heart, color: 'text-purple-600' } ];

const severityLevels = [ { value: 'low', label: 'Low Priority', color: 'bg-green-100 text-green-800' }, { value: 'medium', label: 'Medium Priority', color: 'bg-yellow-100 text-yellow-800' }, { value: 'high', label: 'High Priority', color: 'bg-orange-100 text-orange-800' }, { value: 'critical', label: 'Critical/Urgent', color: 'bg-red-100 text-red-800' } ];

const statusTypes = [ { value: 'submitted', label: 'Submitted', color: 'bg-blue-100 text-blue-800' }, { value: 'reviewing', label: 'Under Review', color: 'bg-yellow-100 text-yellow-800' }, { value: 'investigating', label: 'Investigating', color: 'bg-orange-100 text-orange-800' }, { value: 'escalated', label: 'Escalated', color: 'bg-red-100 text-red-800' }, { value: 'resolved', label: 'Resolved', color: 'bg-green-100 text-green-800' }, { value: 'closed', label: 'Closed', color: 'bg-gray-100 text-gray-800' } ];

const handleSubmit = () => { if (!formData.type || !formData.severity || !formData.title || !formData.description) { alert('Please fill in all required fields'); return; } const newReport = { id: RPT-${String(reports.length + 1).padStart(3, '0')}, ...formData, submittedAt: new Date().toISOString().split('T')[0], lastUpdated: new Date().toISOString().split('T')[0], status: 'submitted', department: formData.type === 'academic' ? 'Academic Affairs' : formData.type === 'ragging' ? 'Disciplinary Committee' : 'Student Affairs' };

setReports([...reports, newReport]);
setFormData({
  type: '',
  severity: '',
  title: '',
  description: '',
  location: '',
  datetime: '',
  witnesses: '',
  anonymous: true,
  contactInfo: ''
});

alert(`Report submitted successfully! Your tracking ID is: ${newReport.id}`);

};

const getStatusColor = (status) => { const statusObj = statusTypes.find(s => s.value === status); return statusObj ? statusObj.color : 'bg-gray-100 text-gray-800'; };

const getSeverityColor = (severity) => { const severityObj = severityLevels.find(s => s.value === severity); return severityObj ? severityObj.color : 'bg-gray-100 text-gray-800'; };

const filteredReports = reports.filter(report => { const matchesType = !searchFilter.type || report.type === searchFilter.type; const matchesStatus = !searchFilter.status || report.status === searchFilter.status; const matchesSeverity = !searchFilter.severity || report.severity === searchFilter.severity; const matchesTracking = !trackingId || report.id.toLowerCase().includes(trackingId.toLowerCase());

return matchesType && matchesStatus && matchesSeverity && matchesTracking;

});

return (

{/* Header */}

SafeSpeak

Anonymous Student Reporting Platform

<button onClick={() => setCurrentView('report')} className={px-4 py-2 rounded-lg font-medium transition-colors ${ currentView === 'report' ? 'bg-indigo-600 text-white' : 'bg-gray-100 text-gray-700 hover:bg-gray-200' }} > Submit Report <button onClick={() => setCurrentView('track')} className={px-4 py-2 rounded-lg font-medium transition-colors ${ currentView === 'track' ? 'bg-indigo-600 text-white' : 'bg-gray-100 text-gray-700 hover:bg-gray-200' }} > Track Reports <button onClick={() => setCurrentView('dashboard')} className={px-4 py-2 rounded-lg font-medium transition-colors ${ currentView === 'dashboard' ? 'bg-indigo-600 text-white' : 'bg-gray-100 text-gray-700 hover:bg-gray-200' }} > Admin Dashboard

  <div className="max-w-7xl mx-auto px-4 py-8">
    {/* Submit Report View */}
    {currentView === 'report' && (
      <div className="max-w-4xl mx-auto">
        <div className="bg-white rounded-xl shadow-xl p-8">
          <div className="mb-8">
            <h2 className="text-3xl font-bold text-gray-900 mb-2">Submit a Report</h2>
            <p className="text-gray-600">Your privacy and safety are our priority. All reports are handled confidentially.</p>
          </div>

          {/* Issue Type Selection */}
          <div className="mb-8">
            <label className="block text-lg font-semibold text-gray-700 mb-4">What type of issue are you reporting?</label>
            <div className="grid grid-cols-2 md:grid-cols-3 gap-4">
              {issueTypes.map((type) => (
                <button
                  key={type.value}
                  onClick={() => setFormData({...formData, type: type.value})}
                  className={`p-4 rounded-lg border-2 transition-all ${
                    formData.type === type.value
                      ? 'border-indigo-500 bg-indigo-50'
                      : 'border-gray-200 hover:border-gray-300'
                  }`}
                >
                  <type.icon className={`w-6 h-6 mb-2 ${type.color}`} />
                  <div className="font-medium text-gray-900">{type.label}</div>
                </button>
              ))}
            </div>
          </div>

          <div className="space-y-6">
            {/* Severity Level */}
            <div>
              <label className="block text-sm font-medium text-gray-700 mb-2">Priority Level</label>
              <select
                value={formData.severity}
                onChange={(e) => setFormData({...formData, severity: e.target.value})}
                className="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500"
                required
              >
                <option value="">Select priority level</option>
                {severityLevels.map((level) => (
                  <option key={level.value} value={level.value}>{level.label}</option>
                ))}
              </select>
            </div>

            {/* Title */}
            <div>
              <label className="block text-sm font-medium text-gray-700 mb-2">Report Title</label>
              <input
                type="text"
                value={formData.title}
                onChange={(e) => setFormData({...formData, title: e.target.value})}
                className="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500"
                placeholder="Brief description of the issue"
                required
              />
            </div>

            {/* Description */}
            <div>
              <label className="block text-sm font-medium text-gray-700 mb-2">Detailed Description</label>
              <textarea
                value={formData.description}
                onChange={(e) => setFormData({...formData, description: e.target.value})}
                rows={6}
                className="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500"
                placeholder="Please provide as much detail as possible about the incident..."
                required
              />
            </div>

            {/* Location and Time */}
            <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
              <div>
                <label className="block text-sm font-medium text-gray-700 mb-2">Location</label>
                <input
                  type="text"
                  value={formData.location}
                  onChange={(e) => setFormData({...formData, location: e.target.value})}
                  className="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500"
                  placeholder="Where did this occur?"
                />
              </div>
              <div>
                <label className="block text-sm font-medium text-gray-700 mb-2">Date & Time</label>
                <input
                  type="datetime-local"
                  value={formData.datetime}
                  onChange={(e) => setFormData({...formData, datetime: e.target.value})}
                  className="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500"
                />
              </div>
            </div>

            {/* Witnesses */}
            <div>
              <label className="block text-sm font-medium text-gray-700 mb-2">Witnesses (Optional)</label>
              <textarea
                value={formData.witnesses}
                onChange={(e) => setFormData({...formData, witnesses: e.target.value})}
                rows={3}
                className="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500"
                placeholder="Were there any witnesses? (Names, descriptions, etc.)"
              />
            </div>

            {/* Anonymous Toggle */}
            <div className="bg-gray-50 p-4 rounded-lg">
              <div className="flex items-center justify-between">
                <div className="flex items-center space-x-3">
                  {formData.anonymous ? <EyeOff className="w-5 h-5 text-indigo-600" /> : <Eye className="w-5 h-5 text-gray-400" />}
                  <div>
                    <p className="font-medium text-gray-900">Submit Anonymously</p>
                    <p className="text-sm text-gray-600">Your identity will be completely protected</p>
                  </div>
                </div>
                <button
                  type="button"
                  onClick={() => setFormData({...formData, anonymous: !formData.anonymous})}
                  className={`w-12 h-6 rounded-full transition-colors ${
                    formData.anonymous ? 'bg-indigo-600' : 'bg-gray-300'
                  }`}
                >
                  <div className={`w-5 h-5 bg-white rounded-full transition-transform ${
                    formData.anonymous ? 'translate-x-6' : 'translate-x-1'
                  }`} />
                </button>
              </div>
            </div>

            {/* Contact Info (if not anonymous) */}
            {!formData.anonymous && (
              <div>
                <label className="block text-sm font-medium text-gray-700 mb-2">Contact Information</label>
                <input
                  type="text"
                  value={formData.contactInfo}
                  onChange={(e) => setFormData({...formData, contactInfo: e.target.value})}
                  className="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500"
                  placeholder="Email or phone number for follow-up"
                />
              </div>
            )}

            {/* Submit Button */}
            <button
              onClick={handleSubmit}
              className="w-full bg-indigo-600 text-white py-4 rounded-lg font-semibold hover:bg-indigo-700 transition-colors flex items-center justify-center space-x-2"
            >
              <Send className="w-5 h-5" />
              <span>Submit Report</span>
            </button>
          </div>
        </div>
      </div>
    )}

    {/* Track Reports View */}
    {currentView === 'track' && (
      <div className="max-w-4xl mx-auto">
        <div className="bg-white rounded-xl shadow-xl p-8">
          <h2 className="text-3xl font-bold text-gray-900 mb-6">Track Your Report</h2>
          
          <div className="mb-6">
            <label className="block text-sm font-medium text-gray-700 mb-2">Enter Tracking ID</label>
            <div className="flex space-x-4">
              <input
                type="text"
                value={trackingId}
                onChange={(e) => setTrackingId(e.target.value)}
                className="flex-1 p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500"
                placeholder="e.g., RPT-001"
              />
              <button className="bg-indigo-600 text-white px-6 py-3 rounded-lg hover:bg-indigo-700 transition-colors">
                <Search className="w-5 h-5" />
              </button>
            </div>
          </div>

          {/* Report Results */}
          {filteredReports.length > 0 && (
            <div className="space-y-4">
              {filteredReports.map((report) => (
                <div key={report.id} className="border border-gray-200 rounded-lg p-6">
                  <div className="flex justify-between items-start mb-4">
                    <div>
                      <h3 className="text-lg font-semibold text-gray-900">{report.title}</h3>
                      <p className="text-sm text-gray-600">ID: {report.id}</p>
                    </div>
                    <div className="flex space-x-2">
                      <span className={`px-3 py-1 rounded-full text-sm font-medium ${getSeverityColor(report.severity)}`}>
                        {severityLevels.find(s => s.value === report.severity)?.label}
                      </span>
                      <span className={`px-3 py-1 rounded-full text-sm font-medium ${getStatusColor(report.status)}`}>
                        {statusTypes.find(s => s.value === report.status)?.label}
                      </span>
                    </div>
                  </div>
                  
                  <div className="grid grid-cols-1 md:grid-cols-3 gap-4 text-sm">
                    <div>
                      <p className="font-medium text-gray-700">Submitted</p>
                      <p className="text-gray-600">{report.submittedAt}</p>
                    </div>
                    <div>
                      <p className="font-medium text-gray-700">Last Updated</p>
                      <p className="text-gray-600">{report.lastUpdated}</p>
                    </div>
                    <div>
                      <p className="font-medium text-gray-700">Department</p>
                      <p className="text-gray-600">{report.department}</p>
                    </div>
                  </div>
                </div>
              ))}
            </div>
          )}
        </div>
      </div>
    )}

    {/* Admin Dashboard View */}
    {currentView === 'dashboard' && (
      <div>
        <div className="bg-white rounded-xl shadow-xl p-8 mb-8">
          <h2 className="text-3xl font-bold text-gray-900 mb-6">Admin Dashboard</h2>
          
          {/* Stats */}
          <div className="grid grid-cols-1 md:grid-cols-4 gap-6 mb-8">
            <div className="bg-blue-50 p-6 rounded-lg">
              <div className="flex items-center justify-between">
                <div>
                  <p className="text-blue-600 font-medium">Total Reports</p>
                  <p className="text-3xl font-bold text-blue-900">{reports.length}</p>
                </div>
                <AlertCircle className="w-8 h-8 text-blue-600" />
              </div>
            </div>
            
            <div className="bg-yellow-50 p-6 rounded-lg">
              <div className="flex items-center justify-between">
                <div>
                  <p className="text-yellow-600 font-medium">Pending</p>
                  <p className="text-3xl font-bold text-yellow-900">
                    {reports.filter(r => ['submitted', 'reviewing', 'investigating'].includes(r.status)).length}
                  </p>
                </div>
                <Clock className="w-8 h-8 text-yellow-600" />
              </div>
            </div>
            
            <div className="bg-red-50 p-6 rounded-lg">
              <div className="flex items-center justify-between">
                <div>
                  <p className="text-red-600 font-medium">Critical</p>
                  <p className="text-3xl font-bold text-red-900">
                    {reports.filter(r => r.severity === 'critical').length}
                  </p>
                </div>
                <AlertTriangle className="w-8 h-8 text-red-600" />
              </div>
            </div>
            
            <div className="bg-green-50 p-6 rounded-lg">
              <div className="flex items-center justify-between">
                <div>
                  <p className="text-green-600 font-medium">Resolved</p>
                  <p className="text-3xl font-bold text-green-900">
                    {reports.filter(r => r.status === 'resolved').length}
                  </p>
                </div>
                <CheckCircle className="w-8 h-8 text-green-600" />
              </div>
            </div>
          </div>

          {/* Filters */}
          <div className="flex flex-wrap gap-4 mb-6">
            <select
              value={searchFilter.type}
              onChange={(e) => setSearchFilter({...searchFilter, type: e.target.value})}
              className="p-2 border border-gray-300 rounded-lg"
            >
              <option value="">All Types</option>
              {issueTypes.map((type) => (
                <option key={type.value} value={type.value}>{type.label}</option>
              ))}
            </select>
            
            <select
              value={searchFilter.status}
              onChange={(e) => setSearchFilter({...searchFilter, status: e.target.value})}
              className="p-2 border border-gray-300 rounded-lg"
            >
              <option value="">All Status</option>
              {statusTypes.map((status) => (
                <option key={status.value} value={status.value}>{status.label}</option>
              ))}
            </select>
            
            <select
              value={searchFilter.severity}
              onChange={(e) => setSearchFilter({...searchFilter, severity: e.target.value})}
              className="p-2 border border-gray-300 rounded-lg"
            >
              <option value="">All Priorities</option>
              {severityLevels.map((level) => (
                <option key={level.value} value={level.value}>{level.label}</option>
              ))}
            </select>
          </div>

          {/* Reports Table */}
          <div className="overflow-x-auto">
            <table className="w-full">
              <thead>
                <tr className="border-b border-gray-200">
                  <th className="text-left py-3 px-4 font-medium text-gray-700">ID</th>
                  <th className="text-left py-3 px-4 font-medium text-gray-700">Title</th>
                  <th className="text-left py-3 px-4 font-medium text-gray-700">Type</th>
                  <th className="text-left py-3 px-4 font-medium text-gray-700">Severity</th>
                  <th className="text-left py-3 px-4 font-medium text-gray-700">Status</th>
                  <th className="text-left py-3 px-4 font-medium text-gray-700">Department</th>
                  <th className="text-left py-3 px-4 font-medium text-gray-700">Date</th>
                </tr>
              </thead>
              <tbody>
                {filteredReports.map((report) => (
                  <tr key={report.id} className="border-b border-gray-100 hover:bg-gray-50">
                    <td className="py-3 px-4 font-medium text-indigo-600">{report.id}</td>
                    <td className="py-3 px-4">{report.title}</td>
                    <td className="py-3 px-4 capitalize">{report.type.replace('_', ' ')}</td>
                    <td className="py-3 px-4">
                      <span className={`px-2 py-1 rounded-full text-xs font-medium ${getSeverityColor(report.severity)}`}>
                        {report.severity}
                      </span>
                    </td>
                    <td className="py-3 px-4">
                      <span className={`px-2 py-1 rounded-full text-xs font-medium ${getStatusColor(report.status)}`}>
                        {report.status}
                      </span>
                    </td>
                    <td className="py-3 px-4">{report.department}</td>
                    <td className="py-3 px-4">{report.submittedAt}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </div>
      </div>
    )}
  </div>
</div>

); };

export default StudentReportingPlatform;

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors