Add proper CSS styling to MyMessages page
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed

This commit is contained in:
dvirlabs 2026-05-10 04:20:27 +03:00
parent 4bc69f4d60
commit 8e617c1998
2 changed files with 383 additions and 175 deletions

View File

@ -0,0 +1,291 @@
.messages-page {
min-height: 100vh;
background-color: #f5f5f5;
padding: 2rem 0;
}
.messages-container {
max-width: 1200px;
margin: 0 auto;
padding: 0 1rem;
}
.messages-header {
margin-bottom: 2rem;
}
.messages-header h1 {
font-size: 2.5rem;
font-weight: bold;
color: #333;
margin-bottom: 0.5rem;
}
.messages-header p {
color: #666;
font-size: 1.1rem;
}
.error-message {
background-color: #fee;
border: 1px solid #fcc;
color: #c33;
padding: 1rem;
border-radius: 8px;
margin-bottom: 1.5rem;
}
.no-messages {
background: white;
border-radius: 12px;
padding: 3rem;
text-align: center;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.no-messages-icon {
font-size: 4rem;
margin-bottom: 1rem;
}
.no-messages h3 {
font-size: 1.5rem;
color: #333;
margin-bottom: 0.5rem;
}
.no-messages p {
color: #666;
margin-bottom: 1.5rem;
}
.messages-list {
display: flex;
flex-direction: column;
gap: 1.5rem;
}
.message-card {
background: white;
border-radius: 12px;
padding: 1.5rem;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
transition: box-shadow 0.3s ease;
}
.message-card:hover {
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
}
.message-card-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 1.5rem;
padding-bottom: 1rem;
border-bottom: 1px solid #e5e5e5;
}
.message-title-section {
flex: 1;
}
.message-title-section h3 {
font-size: 1.4rem;
font-weight: 600;
color: #333;
margin: 0 0 0.5rem 0;
}
.message-date {
font-size: 0.9rem;
color: #888;
margin: 0;
}
.status-badge {
padding: 0.5rem 1rem;
border-radius: 20px;
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.message-content {
margin-bottom: 1.5rem;
}
.message-content h4 {
font-size: 1rem;
font-weight: 600;
color: #555;
margin-bottom: 0.75rem;
}
.message-box {
background: #f9f9f9;
border-radius: 8px;
padding: 1.25rem;
border-left: 4px solid #007bff;
}
.message-box p {
margin: 0;
color: #333;
line-height: 1.6;
white-space: pre-wrap;
}
.admin-response {
margin-top: 1.5rem;
padding-top: 1.5rem;
border-top: 2px solid #e5e5e5;
}
.admin-response h4 {
font-size: 1rem;
font-weight: 600;
color: #10b981;
margin-bottom: 0.75rem;
display: flex;
align-items: center;
}
.response-icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
background: #10b981;
color: white;
border-radius: 50%;
margin-right: 0.5rem;
font-size: 0.75rem;
font-weight: bold;
}
.response-box {
background: #f0fdf4;
border: 1px solid #86efac;
border-radius: 8px;
padding: 1.25rem;
border-left: 4px solid #10b981;
}
.response-box p {
margin: 0;
color: #333;
line-height: 1.6;
white-space: pre-wrap;
}
.waiting-response {
margin-top: 1.5rem;
padding-top: 1.5rem;
border-top: 2px solid #e5e5e5;
}
.waiting-response p {
color: #888;
font-style: italic;
margin: 0;
}
.message-details-section {
margin-top: 1.5rem;
padding-top: 1.5rem;
border-top: 1px solid #e5e5e5;
}
.details-toggle {
background: none;
border: none;
color: #007bff;
font-size: 0.9rem;
cursor: pointer;
padding: 0.5rem 0;
display: flex;
align-items: center;
gap: 0.5rem;
transition: color 0.2s ease;
}
.details-toggle:hover {
color: #0056b3;
}
.message-details {
margin-top: 1rem;
padding: 1rem;
background: #f9f9f9;
border-radius: 8px;
}
.details-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
}
.detail-item {
display: flex;
flex-direction: column;
gap: 0.25rem;
}
.detail-item strong {
font-weight: 600;
color: #555;
font-size: 0.9rem;
}
.detail-item span {
color: #666;
font-size: 0.95rem;
}
.messages-actions {
margin-top: 2rem;
text-align: center;
}
.btn-secondary {
background-color: #6c757d;
color: white;
}
.btn-secondary:hover {
background-color: #5a6268;
}
/* Loading state */
.loading {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5rem;
color: #666;
}
/* Responsive design */
@media (max-width: 768px) {
.messages-header h1 {
font-size: 2rem;
}
.message-card-header {
flex-direction: column;
gap: 1rem;
}
.status-badge {
align-self: flex-start;
}
.details-grid {
grid-template-columns: 1fr;
}
}

View File

@ -1,6 +1,7 @@
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import api from '../api'; import api from '../api';
import './MyMessages.css';
function MyMessages() { function MyMessages() {
const [messages, setMessages] = useState([]); const [messages, setMessages] = useState([]);
@ -35,13 +36,13 @@ function MyMessages() {
} }
}; };
const getStatusBadge = (status) => { const getStatusColor = (status) => {
const statusColors = { const colors = {
new: 'bg-blue-100 text-blue-800', new: '#3b82f6',
read: 'bg-yellow-100 text-yellow-800', read: '#f59e0b',
replied: 'bg-green-100 text-green-800', replied: '#10b981',
}; };
return statusColors[status] || 'bg-gray-100 text-gray-800'; return colors[status] || '#6b7280';
}; };
const formatDate = (dateString) => { const formatDate = (dateString) => {
@ -55,208 +56,122 @@ function MyMessages() {
}; };
if (loading) { if (loading) {
return ( return <div className="loading">Loading your messages...</div>;
<div className="min-h-screen flex items-center justify-center">
<div className="text-xl">Loading your messages...</div>
</div>
);
} }
return ( return (
<div className="min-h-screen bg-gray-100 py-8"> <div className="messages-page">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> <div className="messages-container">
<div className="mb-8"> <div className="messages-header">
<h1 className="text-3xl font-bold text-gray-900">My Messages</h1> <h1>My Messages</h1>
<p className="mt-2 text-gray-600"> <p>View your contact messages and admin responses</p>
View your contact messages and admin responses
</p>
</div> </div>
{error && ( {error && (
<div className="mb-4 bg-red-50 border border-red-200 text-red-600 px-4 py-3 rounded"> <div className="error-message">
{error} {error}
</div> </div>
)} )}
{messages.length === 0 ? ( {messages.length === 0 ? (
<div className="bg-white rounded-lg shadow p-8 text-center"> <div className="no-messages">
<svg <div className="no-messages-icon">💬</div>
className="mx-auto h-12 w-12 text-gray-400" <h3>No messages yet</h3>
fill="none" <p>You haven't sent any contact messages yet.</p>
stroke="currentColor" <button onClick={() => navigate('/contact')} className="btn">
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M8 10h.01M12 10h.01M16 10h.01M9 16H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-5l-5 5v-5z"
/>
</svg>
<h3 className="mt-2 text-lg font-medium text-gray-900">
No messages yet
</h3>
<p className="mt-1 text-gray-500">
You haven't sent any contact messages yet.
</p>
<button
onClick={() => navigate('/contact')}
className="mt-4 px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
>
Send a Message Send a Message
</button> </button>
</div> </div>
) : ( ) : (
<div className="space-y-4"> <div className="messages-list">
{messages.map((message) => ( {messages.map((message) => (
<div <div key={message.id} className="message-card">
key={message.id} <div className="message-card-header">
className="bg-white rounded-lg shadow hover:shadow-md transition-shadow" <div className="message-title-section">
> <h3>{message.subject}</h3>
<div className="p-6"> <p className="message-date">Sent on {formatDate(message.created_at)}</p>
{/* Header */}
<div className="flex justify-between items-start mb-4">
<div className="flex-1">
<h3 className="text-lg font-semibold text-gray-900">
{message.subject}
</h3>
<p className="text-sm text-gray-500 mt-1">
Sent on {formatDate(message.created_at)}
</p>
</div> </div>
<span <span
className={`px-3 py-1 rounded-full text-xs font-medium ${getStatusBadge( className="status-badge"
message.status style={{
)}`} backgroundColor: getStatusColor(message.status) + '20',
color: getStatusColor(message.status),
border: `1px solid ${getStatusColor(message.status)}`
}}
> >
{message.status.toUpperCase()} {message.status.toUpperCase()}
</span> </span>
</div> </div>
{/* Original Message */} <div className="message-content">
<div className="mb-4"> <h4>Your Message:</h4>
<h4 className="text-sm font-medium text-gray-700 mb-2"> <div className="message-box">
Your Message: <p>{message.message}</p>
</h4>
<div className="bg-gray-50 rounded-lg p-4">
<p className="text-gray-700 whitespace-pre-wrap">
{message.message}
</p>
</div> </div>
</div> </div>
{/* Admin Response */}
{message.admin_notes && ( {message.admin_notes && (
<div className="border-t pt-4"> <div className="admin-response">
<h4 className="text-sm font-medium text-green-700 mb-2 flex items-center"> <h4>
<svg <span className="response-icon"></span>
className="w-5 h-5 mr-2"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
fillRule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
clipRule="evenodd"
/>
</svg>
Admin Response: Admin Response:
</h4> </h4>
<div className="bg-green-50 rounded-lg p-4 border border-green-200"> <div className="response-box">
<p className="text-gray-700 whitespace-pre-wrap"> <p>{message.admin_notes}</p>
{message.admin_notes}
</p>
</div> </div>
</div> </div>
)} )}
{!message.admin_notes && message.status === 'new' && ( {!message.admin_notes && message.status === 'new' && (
<div className="border-t pt-4"> <div className="waiting-response">
<p className="text-sm text-gray-500 italic"> <p> Waiting for admin response...</p>
Waiting for admin response...
</p>
</div> </div>
)} )}
{/* Message Details */} <div className="message-details-section">
<div className="mt-4 pt-4 border-t">
<button <button
onClick={() => onClick={() =>
setSelectedMessage( setSelectedMessage(
selectedMessage?.id === message.id ? null : message selectedMessage?.id === message.id ? null : message
) )
} }
className="text-sm text-blue-600 hover:text-blue-800 flex items-center" className="details-toggle"
> >
{selectedMessage?.id === message.id {selectedMessage?.id === message.id ? '▲ Hide Details' : '▼ Show Details'}
? 'Hide Details'
: 'Show Details'}
<svg
className={`w-4 h-4 ml-1 transform transition-transform ${
selectedMessage?.id === message.id
? 'rotate-180'
: ''
}`}
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M19 9l-7 7-7-7"
/>
</svg>
</button> </button>
{selectedMessage?.id === message.id && ( {selectedMessage?.id === message.id && (
<div className="mt-4 space-y-2 text-sm"> <div className="message-details">
<div className="grid grid-cols-2 gap-4"> <div className="details-grid">
<div> <div className="detail-item">
<span className="font-medium text-gray-700"> <strong>Name:</strong>
Name: <span>{message.full_name}</span>
</span>
<p className="text-gray-600">{message.full_name}</p>
</div> </div>
<div> <div className="detail-item">
<span className="font-medium text-gray-700"> <strong>Email:</strong>
Email: <span>{message.email}</span>
</span>
<p className="text-gray-600">{message.email}</p>
</div> </div>
{message.phone && ( {message.phone && (
<div> <div className="detail-item">
<span className="font-medium text-gray-700"> <strong>Phone:</strong>
Phone: <span>{message.phone}</span>
</span>
<p className="text-gray-600">{message.phone}</p>
</div> </div>
)} )}
<div> <div className="detail-item">
<span className="font-medium text-gray-700"> <strong>Read Status:</strong>
Status: <span>{message.is_read ? 'Read' : 'Unread'}</span>
</span>
<p className="text-gray-600">
{message.is_read ? 'Read' : 'Unread'}
</p>
</div> </div>
</div> </div>
</div> </div>
)} )}
</div> </div>
</div> </div>
</div>
))} ))}
</div> </div>
)} )}
{/* Back Button */} <div className="messages-actions">
<div className="mt-8 text-center"> <button onClick={() => navigate('/')} className="btn btn-secondary">
<button
onClick={() => navigate('/')}
className="px-6 py-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300"
>
Back to Home Back to Home
</button> </button>
</div> </div>
@ -266,3 +181,5 @@ function MyMessages() {
} }
export default MyMessages; export default MyMessages;
export default MyMessages;