@visns-studio/visns-components 5.5.4 → 5.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +132 -0
- package/package.json +1 -1
- package/src/components/crm/DataGrid.jsx +18 -1
- package/src/components/crm/auth/ClientLogin.jsx +126 -0
- package/src/components/crm/auth/ClientOTPVerify.jsx +257 -0
- package/src/components/crm/auth/styles/ClientAuth.module.scss +167 -0
- package/src/components/crm/client/ClientDashboard.jsx +62 -0
- package/src/components/crm/client/ClientPortal.jsx +158 -0
- package/src/components/crm/client/styles/ClientDashboard.module.scss +133 -0
- package/src/components/crm/client/styles/ClientPortal.module.scss +240 -0
- package/src/components/crm/generic/GenericAuth.jsx +175 -0
- package/src/components/crm/generic/GenericClientPortal.jsx +165 -0
- package/src/components/crm/generic/styles/GenericClientPortal.module.scss +154 -0
- package/src/index.js +10 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
// Client Auth styles
|
|
2
|
+
.container {
|
|
3
|
+
min-height: 100vh;
|
|
4
|
+
display: flex;
|
|
5
|
+
align-items: center;
|
|
6
|
+
justify-content: center;
|
|
7
|
+
background-color: #f5f5f5;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.loginBox {
|
|
11
|
+
width: 100%;
|
|
12
|
+
max-width: 500px;
|
|
13
|
+
padding: 2.5rem;
|
|
14
|
+
margin: 0 auto;
|
|
15
|
+
background-color: white;
|
|
16
|
+
border-radius: 8px;
|
|
17
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.logo {
|
|
21
|
+
display: block;
|
|
22
|
+
width: 100%;
|
|
23
|
+
max-width: 240px;
|
|
24
|
+
height: auto;
|
|
25
|
+
margin: 0 auto 2rem;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.title {
|
|
29
|
+
font-size: 1.75rem;
|
|
30
|
+
font-weight: 600;
|
|
31
|
+
color: #333;
|
|
32
|
+
margin-bottom: 0.5rem;
|
|
33
|
+
text-align: center;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.subtitle {
|
|
37
|
+
font-size: 1rem;
|
|
38
|
+
color: #666;
|
|
39
|
+
margin-bottom: 2rem;
|
|
40
|
+
text-align: center;
|
|
41
|
+
line-height: 1.5;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.formGroup {
|
|
45
|
+
margin-bottom: 1.5rem;
|
|
46
|
+
position: relative;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.inputLabel {
|
|
50
|
+
display: block;
|
|
51
|
+
font-size: 0.9rem;
|
|
52
|
+
font-weight: 500;
|
|
53
|
+
color: #555;
|
|
54
|
+
margin-bottom: 0.5rem;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.input {
|
|
58
|
+
width: 100%;
|
|
59
|
+
padding: 0.75rem 1rem;
|
|
60
|
+
font-size: 1rem;
|
|
61
|
+
border: 1px solid #ddd;
|
|
62
|
+
border-radius: 4px;
|
|
63
|
+
background-color: white;
|
|
64
|
+
transition: border-color 0.2s ease;
|
|
65
|
+
|
|
66
|
+
&:focus {
|
|
67
|
+
outline: none;
|
|
68
|
+
border-color: var(--primary-color, #4a6741);
|
|
69
|
+
box-shadow: 0 0 0 2px rgba(74, 103, 65, 0.1);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
&.error {
|
|
73
|
+
border-color: #e53935;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.inputIcon {
|
|
78
|
+
position: absolute;
|
|
79
|
+
right: 1rem;
|
|
80
|
+
top: 2.4rem;
|
|
81
|
+
color: #999;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.button {
|
|
85
|
+
width: 100%;
|
|
86
|
+
padding: 0.9rem 1.5rem;
|
|
87
|
+
font-size: 1rem;
|
|
88
|
+
font-weight: 500;
|
|
89
|
+
color: white;
|
|
90
|
+
background-color: var(--primary-color, #0f4229);
|
|
91
|
+
border: none;
|
|
92
|
+
border-radius: 4px;
|
|
93
|
+
cursor: pointer;
|
|
94
|
+
transition: background-color 0.2s ease;
|
|
95
|
+
|
|
96
|
+
&:hover {
|
|
97
|
+
background-color: var(--secondary-color, #0a2e1c);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
&:disabled {
|
|
101
|
+
background-color: #ccc;
|
|
102
|
+
cursor: not-allowed;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.linkText {
|
|
107
|
+
display: block;
|
|
108
|
+
text-align: center;
|
|
109
|
+
margin-top: 1.5rem;
|
|
110
|
+
font-size: 0.9rem;
|
|
111
|
+
color: #666;
|
|
112
|
+
|
|
113
|
+
a {
|
|
114
|
+
color: var(--primary-color, #0f4229);
|
|
115
|
+
text-decoration: none;
|
|
116
|
+
font-weight: 500;
|
|
117
|
+
|
|
118
|
+
&:hover {
|
|
119
|
+
text-decoration: underline;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// OTP verification specific styles
|
|
125
|
+
.otpInput {
|
|
126
|
+
letter-spacing: 0.25rem;
|
|
127
|
+
font-size: 1.2rem;
|
|
128
|
+
text-align: center;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.resendLink {
|
|
132
|
+
color: var(--primary-color, #0f4229);
|
|
133
|
+
text-decoration: none;
|
|
134
|
+
font-weight: 500;
|
|
135
|
+
cursor: pointer;
|
|
136
|
+
|
|
137
|
+
&:hover {
|
|
138
|
+
text-decoration: underline;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
&.disabled {
|
|
142
|
+
color: #999;
|
|
143
|
+
cursor: default;
|
|
144
|
+
text-decoration: none;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.emailHighlight {
|
|
149
|
+
font-weight: 600;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.backLink {
|
|
153
|
+
display: inline-flex;
|
|
154
|
+
align-items: center;
|
|
155
|
+
margin-top: 1rem;
|
|
156
|
+
color: #666;
|
|
157
|
+
text-decoration: none;
|
|
158
|
+
font-size: 0.9rem;
|
|
159
|
+
|
|
160
|
+
svg {
|
|
161
|
+
margin-right: 0.5rem;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
&:hover {
|
|
165
|
+
color: var(--primary-color, #0f4229);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import styles from './styles/ClientDashboard.module.scss';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* ClientDashboard - Example dashboard component for client portal
|
|
7
|
+
*
|
|
8
|
+
* This is a simple example of a custom component that can be passed to GenericClientPortal
|
|
9
|
+
*/
|
|
10
|
+
const ClientDashboard = ({ clientProfile }) => {
|
|
11
|
+
return (
|
|
12
|
+
<div className={styles.dashboardContainer}>
|
|
13
|
+
<h1 className={styles.sectionTitle}>Dashboard</h1>
|
|
14
|
+
|
|
15
|
+
<div className={styles.welcomeCard}>
|
|
16
|
+
<h2>Welcome to your Client Portal</h2>
|
|
17
|
+
<p>
|
|
18
|
+
Hello {clientProfile.name || clientProfile.email}! This is your personal dashboard
|
|
19
|
+
where you can access your documents, update your profile, and more.
|
|
20
|
+
</p>
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
<div className={styles.statsGrid}>
|
|
24
|
+
<div className={styles.statCard}>
|
|
25
|
+
<h3>Documents</h3>
|
|
26
|
+
<p className={styles.statNumber}>5</p>
|
|
27
|
+
<p className={styles.statDescription}>Available documents</p>
|
|
28
|
+
</div>
|
|
29
|
+
<div className={styles.statCard}>
|
|
30
|
+
<h3>Messages</h3>
|
|
31
|
+
<p className={styles.statNumber}>2</p>
|
|
32
|
+
<p className={styles.statDescription}>Unread messages</p>
|
|
33
|
+
</div>
|
|
34
|
+
<div className={styles.statCard}>
|
|
35
|
+
<h3>Tasks</h3>
|
|
36
|
+
<p className={styles.statNumber}>3</p>
|
|
37
|
+
<p className={styles.statDescription}>Pending tasks</p>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
<div className={styles.recentActivity}>
|
|
42
|
+
<h2>Recent Activity</h2>
|
|
43
|
+
<ul className={styles.activityList}>
|
|
44
|
+
<li className={styles.activityItem}>
|
|
45
|
+
<span className={styles.activityDate}>Today</span>
|
|
46
|
+
<span className={styles.activityDescription}>Document "Tax Return 2023" was uploaded</span>
|
|
47
|
+
</li>
|
|
48
|
+
<li className={styles.activityItem}>
|
|
49
|
+
<span className={styles.activityDate}>Yesterday</span>
|
|
50
|
+
<span className={styles.activityDescription}>You received a new message</span>
|
|
51
|
+
</li>
|
|
52
|
+
<li className={styles.activityItem}>
|
|
53
|
+
<span className={styles.activityDate}>3 days ago</span>
|
|
54
|
+
<span className={styles.activityDescription}>Your profile was updated</span>
|
|
55
|
+
</li>
|
|
56
|
+
</ul>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export default ClientDashboard;
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Routes, Route, Link } from 'react-router-dom';
|
|
3
|
+
import { toast } from 'react-toastify';
|
|
4
|
+
|
|
5
|
+
import CustomFetch from '../Fetch';
|
|
6
|
+
|
|
7
|
+
import styles from './styles/ClientPortal.module.scss';
|
|
8
|
+
|
|
9
|
+
const ClientPortal = ({ clientProfile, setClientProfile, setClientAuth, logo }) => {
|
|
10
|
+
const handleLogout = async () => {
|
|
11
|
+
try {
|
|
12
|
+
await CustomFetch('/client/logout', 'POST');
|
|
13
|
+
setClientProfile({});
|
|
14
|
+
setClientAuth(false);
|
|
15
|
+
toast.success('You have been logged out successfully');
|
|
16
|
+
} catch (error) {
|
|
17
|
+
toast.error('Error logging out. Please try again.');
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<div className={styles.portalContainer}>
|
|
23
|
+
<header className={styles.portalHeader}>
|
|
24
|
+
<div className={styles.logoContainer}>
|
|
25
|
+
<img src={logo} alt="Client Portal" className={styles.logo} />
|
|
26
|
+
</div>
|
|
27
|
+
<div className={styles.userInfo}>
|
|
28
|
+
<span className={styles.welcomeText}>
|
|
29
|
+
Welcome, <strong>{clientProfile.name || clientProfile.email}</strong>
|
|
30
|
+
</span>
|
|
31
|
+
<button onClick={handleLogout} className={styles.logoutButton}>
|
|
32
|
+
Logout
|
|
33
|
+
</button>
|
|
34
|
+
</div>
|
|
35
|
+
</header>
|
|
36
|
+
|
|
37
|
+
<main className={styles.portalContent}>
|
|
38
|
+
<div className={styles.sidebar}>
|
|
39
|
+
<nav className={styles.navigation}>
|
|
40
|
+
<ul>
|
|
41
|
+
<li>
|
|
42
|
+
<Link to="/client/portal" className={styles.navLink}>
|
|
43
|
+
Dashboard
|
|
44
|
+
</Link>
|
|
45
|
+
</li>
|
|
46
|
+
<li>
|
|
47
|
+
<Link to="/client/portal/profile" className={styles.navLink}>
|
|
48
|
+
My Profile
|
|
49
|
+
</Link>
|
|
50
|
+
</li>
|
|
51
|
+
<li>
|
|
52
|
+
<Link to="/client/portal/documents" className={styles.navLink}>
|
|
53
|
+
Documents
|
|
54
|
+
</Link>
|
|
55
|
+
</li>
|
|
56
|
+
</ul>
|
|
57
|
+
</nav>
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
<div className={styles.mainContent}>
|
|
61
|
+
<Routes>
|
|
62
|
+
<Route
|
|
63
|
+
path="/"
|
|
64
|
+
element={<Dashboard clientProfile={clientProfile} />}
|
|
65
|
+
/>
|
|
66
|
+
<Route
|
|
67
|
+
path="/profile"
|
|
68
|
+
element={<Profile clientProfile={clientProfile} setClientProfile={setClientProfile} />}
|
|
69
|
+
/>
|
|
70
|
+
<Route
|
|
71
|
+
path="/documents"
|
|
72
|
+
element={<Documents clientProfile={clientProfile} />}
|
|
73
|
+
/>
|
|
74
|
+
</Routes>
|
|
75
|
+
</div>
|
|
76
|
+
</main>
|
|
77
|
+
</div>
|
|
78
|
+
);
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// Simple Dashboard component
|
|
82
|
+
const Dashboard = ({ clientProfile }) => {
|
|
83
|
+
return (
|
|
84
|
+
<div className={styles.dashboardContainer}>
|
|
85
|
+
<h1>Dashboard</h1>
|
|
86
|
+
<div className={styles.welcomeCard}>
|
|
87
|
+
<h2>Welcome to your Client Portal</h2>
|
|
88
|
+
<p>
|
|
89
|
+
This is your personal dashboard where you can access your documents,
|
|
90
|
+
update your profile, and more.
|
|
91
|
+
</p>
|
|
92
|
+
</div>
|
|
93
|
+
|
|
94
|
+
<div className={styles.statsGrid}>
|
|
95
|
+
<div className={styles.statCard}>
|
|
96
|
+
<h3>Documents</h3>
|
|
97
|
+
<p className={styles.statNumber}>5</p>
|
|
98
|
+
</div>
|
|
99
|
+
<div className={styles.statCard}>
|
|
100
|
+
<h3>Messages</h3>
|
|
101
|
+
<p className={styles.statNumber}>2</p>
|
|
102
|
+
</div>
|
|
103
|
+
<div className={styles.statCard}>
|
|
104
|
+
<h3>Tasks</h3>
|
|
105
|
+
<p className={styles.statNumber}>3</p>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
);
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
// Simple Profile component
|
|
113
|
+
const Profile = ({ clientProfile, setClientProfile }) => {
|
|
114
|
+
return (
|
|
115
|
+
<div className={styles.profileContainer}>
|
|
116
|
+
<h1>My Profile</h1>
|
|
117
|
+
<div className={styles.profileCard}>
|
|
118
|
+
<div className={styles.profileField}>
|
|
119
|
+
<label>Name</label>
|
|
120
|
+
<p>{clientProfile.name || 'Not provided'}</p>
|
|
121
|
+
</div>
|
|
122
|
+
<div className={styles.profileField}>
|
|
123
|
+
<label>Email</label>
|
|
124
|
+
<p>{clientProfile.email}</p>
|
|
125
|
+
</div>
|
|
126
|
+
<div className={styles.profileField}>
|
|
127
|
+
<label>Phone</label>
|
|
128
|
+
<p>{clientProfile.phone || 'Not provided'}</p>
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
);
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
// Simple Documents component
|
|
136
|
+
const Documents = ({ clientProfile }) => {
|
|
137
|
+
return (
|
|
138
|
+
<div className={styles.documentsContainer}>
|
|
139
|
+
<h1>Documents</h1>
|
|
140
|
+
<p>Your documents will appear here.</p>
|
|
141
|
+
|
|
142
|
+
<div className={styles.documentsList}>
|
|
143
|
+
<div className={styles.documentCard}>
|
|
144
|
+
<h3>Sample Document 1</h3>
|
|
145
|
+
<p>Uploaded on: 2023-05-15</p>
|
|
146
|
+
<button className={styles.viewButton}>View</button>
|
|
147
|
+
</div>
|
|
148
|
+
<div className={styles.documentCard}>
|
|
149
|
+
<h3>Sample Document 2</h3>
|
|
150
|
+
<p>Uploaded on: 2023-06-20</p>
|
|
151
|
+
<button className={styles.viewButton}>View</button>
|
|
152
|
+
</div>
|
|
153
|
+
</div>
|
|
154
|
+
</div>
|
|
155
|
+
);
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
export default ClientPortal;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
// Client Dashboard Styles
|
|
2
|
+
.dashboardContainer {
|
|
3
|
+
width: 100%;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.sectionTitle {
|
|
7
|
+
margin-top: 0;
|
|
8
|
+
margin-bottom: 1rem;
|
|
9
|
+
color: #333;
|
|
10
|
+
font-size: 1.5rem;
|
|
11
|
+
font-weight: 600;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.welcomeCard {
|
|
15
|
+
background-color: white;
|
|
16
|
+
border-radius: 6px;
|
|
17
|
+
padding: 1.25rem;
|
|
18
|
+
margin-bottom: 1.5rem;
|
|
19
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
|
|
20
|
+
border-left: 3px solid var(--primary-color, #0f4229);
|
|
21
|
+
|
|
22
|
+
h2 {
|
|
23
|
+
margin-top: 0;
|
|
24
|
+
color: var(--primary-color, #0f4229);
|
|
25
|
+
font-size: 1.25rem;
|
|
26
|
+
margin-bottom: 0.5rem;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
p {
|
|
30
|
+
color: #666;
|
|
31
|
+
line-height: 1.5;
|
|
32
|
+
margin-bottom: 0;
|
|
33
|
+
font-size: 0.9rem;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.statsGrid {
|
|
38
|
+
display: grid;
|
|
39
|
+
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
|
|
40
|
+
gap: 1rem;
|
|
41
|
+
margin-bottom: 1.5rem;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.statCard {
|
|
45
|
+
background-color: white;
|
|
46
|
+
border-radius: 6px;
|
|
47
|
+
padding: 1.25rem;
|
|
48
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
|
|
49
|
+
text-align: center;
|
|
50
|
+
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
|
51
|
+
|
|
52
|
+
&:hover {
|
|
53
|
+
transform: translateY(-2px);
|
|
54
|
+
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.08);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
h3 {
|
|
58
|
+
margin-top: 0;
|
|
59
|
+
color: #555;
|
|
60
|
+
font-size: 0.9rem;
|
|
61
|
+
font-weight: 500;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.statNumber {
|
|
65
|
+
font-size: 2rem;
|
|
66
|
+
font-weight: 600;
|
|
67
|
+
color: var(--primary-color, #0f4229);
|
|
68
|
+
margin: 0.4rem 0;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.statDescription {
|
|
72
|
+
color: #777;
|
|
73
|
+
font-size: 0.8rem;
|
|
74
|
+
margin: 0;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.recentActivity {
|
|
79
|
+
background-color: white;
|
|
80
|
+
border-radius: 6px;
|
|
81
|
+
padding: 1.25rem;
|
|
82
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
|
|
83
|
+
|
|
84
|
+
h2 {
|
|
85
|
+
margin-top: 0;
|
|
86
|
+
margin-bottom: 0.75rem;
|
|
87
|
+
font-size: 1.1rem;
|
|
88
|
+
color: #333;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.activityList {
|
|
93
|
+
list-style: none;
|
|
94
|
+
padding: 0;
|
|
95
|
+
margin: 0;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.activityItem {
|
|
99
|
+
display: flex;
|
|
100
|
+
padding: 0.75rem 0;
|
|
101
|
+
border-bottom: 1px solid #eee;
|
|
102
|
+
|
|
103
|
+
&:last-child {
|
|
104
|
+
border-bottom: none;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.activityDate {
|
|
108
|
+
flex: 0 0 80px;
|
|
109
|
+
color: #777;
|
|
110
|
+
font-size: 0.8rem;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.activityDescription {
|
|
114
|
+
flex: 1;
|
|
115
|
+
color: #333;
|
|
116
|
+
font-size: 0.9rem;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Responsive styles
|
|
121
|
+
@media (max-width: 768px) {
|
|
122
|
+
.statsGrid {
|
|
123
|
+
grid-template-columns: 1fr;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.activityItem {
|
|
127
|
+
flex-direction: column;
|
|
128
|
+
|
|
129
|
+
.activityDate {
|
|
130
|
+
margin-bottom: 0.5rem;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|