EverydayTech Platform - Developer Reference
Complete Source Code Documentation - All Applications
Loading...
Searching...
No Matches
TabOffice365.jsx
Go to the documentation of this file.
1import { useState, useEffect } from 'react';
2import { apiFetch } from '../../lib/api';
3import PurchaseLicenseModal from '../../components/PurchaseLicenseModal';
4
5export default function TabOffice365() {
6 const [subscriptions, setSubscriptions] = useState([]);
7 const [loading, setLoading] = useState(true);
8 const [error, setError] = useState(null);
9 const [showPurchaseModal, setShowPurchaseModal] = useState(false);
10
11 useEffect(() => {
12 fetchSubscriptions();
13 }, []);
14
15 const fetchSubscriptions = async () => {
16 setLoading(true);
17 setError(null);
18 try {
19 const token = localStorage.getItem('token');
20 const res = await apiFetch('/office365/subscriptions', {
21 headers: { Authorization: `Bearer ${token}` }
22 });
23
24 if (res.ok) {
25 const data = await res.json();
26 setSubscriptions(data.subscriptions || []);
27 } else {
28 throw new Error('Failed to fetch subscriptions');
29 }
30 } catch (err) {
31 console.error('Error fetching Office 365 subscriptions:', err);
32 setError(err.message || 'Error loading subscriptions');
33 } finally {
34 setLoading(false);
35 }
36 };
37
38 const formatCurrency = (amount) => {
39 return new Intl.NumberFormat('en-AU', {
40 style: 'currency',
41 currency: 'AUD'
42 }).format(amount || 0);
43 };
44
45 const formatDate = (dateString) => {
46 if (!dateString) return 'N/A';
47 return new Date(dateString).toLocaleDateString('en-AU');
48 };
49
50 return (
51 <div style={{ padding: '20px' }}>
52 <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '20px' }}>
53 <div>
54 <h2>Office 365 Management</h2>
55 <p style={{ color: 'var(--text-secondary)', marginTop: '8px' }}>
56 Manage Microsoft 365 licenses through Pax8 integration
57 </p>
58 </div>
59 <button
60 className="btn btn-primary"
61 onClick={() => setShowPurchaseModal(true)}
62 disabled={loading}
63 >
64 <span className="material-symbols-outlined">add</span>
65 Purchase License
66 </button>
67 </div>
68
69 {error && (
70 <div style={{
71 padding: '12px',
72 background: '#fee',
73 border: '1px solid #fcc',
74 borderRadius: '4px',
75 color: '#c33',
76 marginBottom: '20px'
77 }}>
78 {error}
79 </div>
80 )}
81
82 <div className="card" style={{ marginTop: '20px' }}>
83 {loading ? (
84 <div style={{ textAlign: 'center', padding: '60px 20px', color: 'var(--text-secondary)' }}>
85 <div className="spinner" style={{ margin: '0 auto 16px' }}></div>
86 <p>Loading subscriptions...</p>
87 </div>
88 ) : subscriptions.length === 0 ? (
89 <div style={{ textAlign: 'center', padding: '60px 20px', color: 'var(--text-secondary)' }}>
90 <span className="material-symbols-outlined" style={{ fontSize: '64px', marginBottom: '16px', display: 'block', opacity: 0.5 }}>
91 cloud_off
92 </span>
93 <h3 style={{ color: 'var(--text)', marginBottom: '8px' }}>No subscriptions yet</h3>
94 <p>Purchase Microsoft 365 licenses through Pax8 to get started</p>
95 <button
96 className="btn btn-primary"
97 onClick={() => setShowPurchaseModal(true)}
98 style={{ marginTop: '20px' }}
99 >
100 <span className="material-symbols-outlined">add</span>
101 Purchase Your First License
102 </button>
103 </div>
104 ) : (
105 <table className="data-table">
106 <thead>
107 <tr>
108 <th>Product</th>
109 <th>Customer</th>
110 <th>Quantity</th>
111 <th>Monthly Cost</th>
112 <th>Status</th>
113 <th>Next Billing</th>
114 <th>Actions</th>
115 </tr>
116 </thead>
117 <tbody>
118 {subscriptions.map((sub) => (
119 <tr key={sub.subscription_id}>
120 <td>
121 <div>
122 <strong>{sub.product_name || 'Unknown Product'}</strong>
123 {sub.sku && (
124 <div style={{ fontSize: '12px', color: '#888' }}>
125 SKU: {sub.sku}
126 </div>
127 )}
128 </div>
129 </td>
130 <td>{sub.customer_name || 'N/A'}</td>
131 <td>{sub.quantity || 0}</td>
132 <td>{formatCurrency(sub.monthly_price)}</td>
133 <td>
134 <span className={`status-badge ${(sub.status || 'active').toLowerCase()}`}>
135 {sub.status || 'Active'}
136 </span>
137 </td>
138 <td>{formatDate(sub.next_billing_date)}</td>
139 <td>
140 <div className="action-buttons">
141 <button
142 className="btn btn-sm"
143 title="View details"
144 onClick={() => alert('View details coming soon')}
145 >
146 <span className="material-symbols-outlined">visibility</span>
147 </button>
148 <button
149 className="btn btn-sm"
150 title="Edit subscription"
151 onClick={() => alert('Edit functionality coming soon')}
152 >
153 <span className="material-symbols-outlined">edit</span>
154 </button>
155 </div>
156 </td>
157 </tr>
158 ))}
159 </tbody>
160 </table>
161 )}
162 </div>
163
164 {/* Purchase Modal */}
165 {showPurchaseModal && (
166 <PurchaseLicenseModal
167 onClose={() => setShowPurchaseModal(false)}
168 onSuccess={() => {
169 fetchSubscriptions();
170 setShowPurchaseModal(false);
171 }}
172 />
173 )}
174 </div>
175 );
176}