File size: 11,578 Bytes
25f22bf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
import React, { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useNavigate } from 'react-router-dom';
import { fetchSources } from '../store/reducers/sourcesSlice';
import { fetchAccounts } from '../store/reducers/accountsSlice';
import { fetchPosts } from '../store/reducers/postsSlice';
import { fetchSchedules } from '../store/reducers/schedulesSlice';

const Dashboard = () => {
  const dispatch = useDispatch();
  const navigate = useNavigate();
  const { user } = useSelector(state => state.auth);
  const { items: sources, loading: sourcesLoading } = useSelector(state => state.sources);
  const { items: accounts, loading: accountsLoading } = useSelector(state => state.accounts);
  const { items: posts, loading: postsLoading } = useSelector(state => state.posts);
  const { items: schedules, loading: schedulesLoading } = useSelector(state => state.schedules);

  const [mounted, setMounted] = useState(false);

  useEffect(() => {
    // Only fetch data if user is authenticated
    if (user) {
      // Fetch all data for the dashboard
      dispatch(fetchSources());
      dispatch(fetchAccounts());
      dispatch(fetchPosts());
      dispatch(fetchSchedules());
      setMounted(true);
    } else {
      // Redirect to login page if user is not authenticated
      navigate('/login');
    }
  }, [dispatch, user, navigate]);

  // Count published posts
  const publishedPosts = posts.filter(post => post.is_published).length;

  // Add error boundary
  if (!user) {
    // Redirect to login page if user is not authenticated
    window.location.href = '/login';
    return null;
  }

  // Loading skeleton for statistics cards
  const StatCard = ({ title, value, icon: Icon, loading }) => (
    <div className="relative overflow-hidden bg-white rounded-2xl shadow-lg hover:shadow-xl transition-all duration-300 transform hover:-translate-y-1 animate-scale-in">
      {loading && (
        <div className="absolute inset-0 bg-gradient-to-r from-transparent via-white/50 to-transparent animate-pulse-slow" />
      )}
      <div className="p-4 sm:p-6">
        <div className="flex items-center justify-between mb-3 sm:mb-4">
          <div className="p-2 sm:p-3 bg-primary-100 rounded-xl">
            <Icon className="w-5 h-5 sm:w-6 sm:h-6 text-primary-600" />
          </div>
        </div>
        <h3 className="text-xs sm:text-sm font-medium text-secondary-600 mb-1">{title}</h3>
        <div className={`text-xl sm:text-3xl font-bold text-secondary-900 ${
          loading ? 'flex items-center space-x-1' : ''
        }`}>
          {loading ? (
            <>
              <div className="w-3 h-4 sm:w-4 sm:h-6 bg-secondary-200 rounded animate-pulse"></div>
              <div className="w-3 h-4 sm:w-4 sm:h-6 bg-secondary-200 rounded animate-pulse"></div>
              <div className="w-3 h-4 sm:w-4 sm:h-6 bg-secondary-200 rounded animate-pulse"></div>
            </>
          ) : (
            value
          )}
        </div>
      </div>
    </div>
  );

  // Activity item component
  const ActivityItem = ({ post }) => (
    <div className="group relative bg-white rounded-xl p-3 sm:p-4 mb-3 hover:shadow-md transition-all duration-300 animate-slide-up">
      <div className="flex items-start space-x-3">
        <div className="flex-shrink-0">
          <div className="w-8 h-8 sm:w-10 sm:h-10 bg-gradient-to-br from-primary-500 to-primary-600 rounded-full flex items-center justify-center">
            <svg className="w-4 h-4 sm:w-5 sm:h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
            </svg>
          </div>
        </div>
        <div className="flex-1 min-w-0">
          <p className="text-xs sm:text-sm text-secondary-800 mb-1 line-clamp-2">
            {post.Text_content ? post.Text_content.substring(0, 100) + '...' : 'No content available'}
          </p>
          <div className="flex flex-col sm:flex-row sm:items-center justify-between gap-2">
            <span className={`inline-flex items-center px-2 py-1 rounded-full text-xs font-medium ${
              post.is_published
                ? 'bg-green-100 text-green-800'
                : 'bg-green-100 text-green-800'
            }`}>
              Published
            </span>
            <span className="text-xs text-secondary-500">
              {post.created_at ? new Date(post.created_at).toLocaleDateString() : 'No date'}
            </span>
          </div>
        </div>
      </div>
    </div>
  );

  return (
    <div className="min-h-screen bg-gradient-to-br from-accent-50 via-white to-primary-50">
      {/* Skip to content link for accessibility */}
      <a href="#main-content" className="sr-only focus:not-sr-only focus:absolute focus:top-2 sm:top-4 focus:left-2 sm:left-4 bg-primary-600 text-white px-3 sm:px-4 py-2 rounded-lg text-sm">
        Skip to main content
      </a>

      <div className="container mx-auto px-3 sm:px-4 py-4 sm:py-8 max-w-7xl">
        {/* Dashboard Header */}
        <header className="mb-6 sm:mb-8 animate-fade-in">
          <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
            <div>
              <h1 className="text-2xl sm:text-4xl font-bold text-secondary-900 mb-1 sm:mb-2">Dashboard</h1>
              <p className="text-base sm:text-lg text-secondary-600">
                Welcome back, <span className="font-semibold text-primary-600">{user?.email}</span>
              </p>
            </div>
            <div className="hidden sm:block">
              <div className="bg-white rounded-2xl shadow-lg p-3 sm:p-4 border border-secondary-200">
                <div className="flex items-center space-x-3">
                  <div className="w-10 h-10 sm:w-12 sm:h-12 bg-gradient-to-br from-primary-500 to-primary-600 rounded-full flex items-center justify-center">
                    <span className="text-white font-semibold text-base sm:text-lg">
                      {user?.email?.charAt(0).toUpperCase()}
                    </span>
                  </div>
                  <div>
                    <p className="text-xs sm:text-sm font-medium text-secondary-900">Account</p>
                    <p className="text-xs text-secondary-600">Active</p>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </header>

        {/* Statistics Cards */}
        <div className="grid grid-cols-2 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-3 gap-3 sm:gap-6 mb-6 sm:mb-8">
          <StatCard
            title="Sources"
            value={sources.length}
            icon={(props) => (
              <svg {...props} fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" />
              </svg>
            )}
            loading={sourcesLoading}
          />
          
          <StatCard
            title="Accounts"
            value={accounts.length}
            icon={(props) => (
              <svg {...props} fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
              </svg>
            )}
            loading={accountsLoading}
          />
          
          <StatCard
            title="Published Posts"
            value={publishedPosts}
            icon={(props) => (
              <svg {...props} fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
              </svg>
            )}
            loading={postsLoading}
          />
        </div>

        {/* Main Content Grid */}
        <div className="grid grid-cols-1 xl:grid-cols-3 gap-6 sm:gap-8">
          {/* Recent Activity Section */}
          <div className="xl:col-span-3">
            <div className="bg-white rounded-2xl shadow-lg p-4 sm:p-6 animate-slide-up">
              <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between mb-4 sm:mb-6 gap-2">
                <h2 className="text-xl sm:text-2xl font-bold text-secondary-900">Recent Activity</h2>
                <button className="text-xs sm:text-sm text-primary-600 hover:text-primary-700 font-medium transition-colors text-left sm:text-right">
                  View All
                </button>
              </div>
              
              <div className="activity-list">
                {postsLoading ? (
                  <div className="space-y-3">
                    {[...Array(3)].map((_, i) => (
                      <div key={i} className="bg-gray-50 rounded-xl p-3 sm:p-4 animate-pulse">
                        <div className="flex items-start space-x-3">
                          <div className="w-8 h-8 sm:w-10 sm:h-10 bg-gray-200 rounded-full flex-shrink-0"></div>
                          <div className="flex-1">
                            <div className="h-3 sm:h-4 bg-gray-200 rounded mb-2"></div>
                            <div className="h-2 sm:h-3 bg-gray-200 rounded w-3/4"></div>
                          </div>
                        </div>
                      </div>
                    ))}
                  </div>
                ) : posts.length === 0 ? (
                  <div className="text-center py-8 sm:py-12">
                    <div className="w-12 h-12 sm:w-16 sm:h-16 bg-accent-100 rounded-full flex items-center justify-center mx-auto mb-3 sm:mb-4">
                      <svg className="w-6 h-6 sm:w-8 sm:h-8 text-accent-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                        <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
                      </svg>
                    </div>
                    <h3 className="text-base sm:text-lg font-semibold text-secondary-900 mb-2">No posts yet</h3>
                    <p className="text-sm sm:text-base text-secondary-600 mb-4">Create your first post to get started!</p>
                    <button
                      onClick={() => navigate('/posts')}
                      className="btn btn-primary text-sm sm:text-base px-4 sm:px-6 py-2 sm:py-3"
                    >
                      Create Your First Post
                    </button>
                  </div>
                ) : (
                  // Sort posts by creation date (newest first)
                  [...posts]
                    .sort((a, b) => new Date(b.created_at) - new Date(a.created_at))
                    .slice(0, 5)
                    .map((post, index) => (
                      <ActivityItem key={post.id} post={post} style={{ animationDelay: `${index * 0.1}s` }} />
                    ))
                )}
              </div>
            </div>
          </div>

          {/* Quick Actions Section - REMOVED */}
          
          {/* Additional Info Card - REMOVED */}
        </div>
      </div>
    </div>
  );
};

export default Dashboard;