codeapp-js 0.1.1 → 0.2.2
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/codeApp/dist/codeapp.js +552 -78
- package/codeApp/dist/power-apps-data.js +2531 -2531
- package/dev files/outlook.js +218 -9
- package/examples/combined demo/dist/codeapp.js +1098 -0
- package/examples/combined demo/dist/index.js +69 -114
- package/examples/combined demo/dist/power-apps-data.js +646 -170
- package/examples/combined demo/power.config.json +42 -42
- package/examples/dataverse Demo/dist/codeapp.js +1085 -0
- package/examples/dataverse Demo/dist/index.html +54 -54
- package/examples/dataverse Demo/dist/index.js +82 -70
- package/examples/dataverse Demo/dist/power-apps-data.js +551 -170
- package/examples/dataverse Demo/power.config.json +34 -34
- package/examples/dataverse Demo/readme.md +79 -79
- package/examples/groups Demo/dist/codeapp.js +1085 -0
- package/examples/groups Demo/dist/index.js +27 -27
- package/examples/groups Demo/dist/power-apps-data.js +551 -170
- package/examples/kanban/dist/dataverse.js +94 -94
- package/examples/kanban/dist/environmentVar.js +55 -55
- package/examples/kanban/dist/office365groups.js +97 -97
- package/examples/kanban/dist/office365users.js +169 -169
- package/examples/kanban/dist/outlook.js +162 -162
- package/examples/kanban/dist/power-apps-data.js +560 -138
- package/examples/kanban/dist/sharepoint.js +339 -339
- package/examples/myProfile/dist/index.html +184 -184
- package/examples/myProfile/dist/index.js +141 -141
- package/examples/myProfile/dist/office365users.js +169 -169
- package/examples/myProfile/dist/power-apps-data.js +560 -138
- package/examples/myProfile/power.config.json +22 -22
- package/examples/myProfile/readme.md +79 -79
- package/examples/outlook Demo/dist/codeapp.js +1085 -0
- package/examples/outlook Demo/dist/index.html +35 -35
- package/examples/outlook Demo/dist/index.js +170 -166
- package/examples/outlook Demo/dist/outlook.js +121 -121
- package/examples/outlook Demo/dist/power-apps-data.js +551 -170
- package/examples/outlook Demo/dist/styles.css +84 -84
- package/examples/outlook Demo/readme.md +82 -82
- package/examples/outlook Demo2/OutlookDemo_1_0_0_1.zip +0 -0
- package/examples/outlook Demo2/agent/decision-log.md +7 -0
- package/examples/outlook Demo2/dist/codeapp.js +1334 -0
- package/examples/outlook Demo2/dist/icon-512.png +0 -0
- package/examples/outlook Demo2/dist/index.html +98 -0
- package/examples/outlook Demo2/dist/index.js +346 -0
- package/examples/outlook Demo2/dist/power-apps-data.js +3007 -0
- package/examples/outlook Demo2/dist/styles.css +639 -0
- package/examples/outlook Demo2/power.config.json +23 -0
- package/examples/outlook Demo2/src/generated/index.ts +14 -0
- package/examples/outlook Demo2/src/generated/models/Office365GroupsModel.ts +363 -0
- package/examples/outlook Demo2/src/generated/models/Office365OutlookModel.ts +2046 -0
- package/examples/outlook Demo2/src/generated/models/Office365UsersModel.ts +254 -0
- package/examples/outlook Demo2/src/generated/services/Office365GroupsService.ts +326 -0
- package/examples/outlook Demo2/src/generated/services/Office365OutlookService.ts +2476 -0
- package/examples/outlook Demo2/src/generated/services/Office365UsersService.ts +358 -0
- package/examples/planning Poker/.vscode/settings.json +4 -4
- package/examples/planning Poker/additional files/customizations (tables).xml +6428 -6428
- package/examples/planning Poker/additional files/dataverse-tables.json +165 -165
- package/examples/planning Poker/additional files/readme.md +122 -122
- package/examples/planning Poker/dist/dataverse.js +78 -78
- package/examples/planning Poker/dist/index.html +198 -198
- package/examples/planning Poker/dist/index.js +954 -954
- package/examples/planning Poker/dist/power-apps-data.js +560 -138
- package/examples/planning Poker/dist/styles.css +815 -815
- package/examples/sharePoint Demo/agent/decision-log.md +5 -5
- package/examples/sharePoint Demo/dist/codeapp.js +1085 -0
- package/examples/sharePoint Demo/dist/index.js +44 -51
- package/examples/sharePoint Demo/dist/power-apps-data.js +551 -170
- package/examples/sharePoint Demo/power.config.json +22 -22
- package/examples/solution explorer/agent/decision-log.md +27 -0
- package/examples/solution explorer/agent/mockup-01-swiss-grid.html +452 -0
- package/examples/solution explorer/agent/mockup-02-dark-glass.html +496 -0
- package/examples/solution explorer/agent/mockup-03-paper-console.html +510 -0
- package/examples/solution explorer/agent/mockup-04-neon-noir.html +546 -0
- package/examples/solution explorer/agent/mockup-05-zen-garden.html +534 -0
- package/examples/solution explorer/dist/codeapp.js +1098 -0
- package/examples/solution explorer/dist/icon-512.png +0 -0
- package/examples/solution explorer/dist/index.html +80 -0
- package/examples/solution explorer/dist/index.js +735 -0
- package/examples/solution explorer/dist/power-apps-data.js +3007 -0
- package/examples/solution explorer/dist/styles.css +571 -0
- package/examples/solution explorer/power.config.json +151 -0
- package/examples/todo/dist/dataverse.js +64 -64
- package/examples/todo/dist/index.html +75 -75
- package/examples/todo/dist/index.js +8 -8
- package/examples/todo/dist/power-apps-data.js +560 -138
- package/examples/todo/dist/renderer.js +375 -375
- package/examples/todo/dist/styles.css +691 -691
- package/examples/todo/power.config.json +34 -34
- package/package.json +1 -8
- package/docs-mockups/atelier/index.html +0 -120
- package/docs-mockups/atelier/script.js +0 -23
- package/docs-mockups/atelier/styles.css +0 -361
- package/docs-mockups/field-guide/index.html +0 -112
- package/docs-mockups/field-guide/script.js +0 -20
- package/docs-mockups/field-guide/styles.css +0 -272
- package/docs-mockups/index.html +0 -80
- package/docs-mockups/maker-hub/index.html +0 -178
- package/docs-mockups/maker-hub/script.js +0 -20
- package/docs-mockups/maker-hub/styles.css +0 -404
- package/docs-mockups/script.js +0 -26
- package/docs-mockups/signal/index.html +0 -146
- package/docs-mockups/signal/script.js +0 -20
- package/docs-mockups/signal/styles.css +0 -314
- package/docs-mockups/styles.css +0 -287
- package/examples/combined demo/dist/dataverse.js +0 -86
- package/examples/combined demo/dist/environmentVar.js +0 -55
- package/examples/combined demo/dist/office365groups.js +0 -97
- package/examples/combined demo/dist/office365users.js +0 -169
- package/examples/combined demo/dist/outlook.js +0 -162
- package/examples/combined demo/dist/sharepoint.js +0 -339
- package/examples/dataverse Demo/dist/dataverse.js +0 -86
- package/examples/groups Demo/dist/dataverse.js +0 -86
- package/examples/groups Demo/dist/environmentVar.js +0 -55
- package/examples/groups Demo/dist/office365groups.js +0 -97
- package/examples/groups Demo/dist/office365users.js +0 -169
- package/examples/groups Demo/dist/outlook.js +0 -162
- package/examples/groups Demo/dist/sharepoint.js +0 -339
- package/examples/sharePoint Demo/dist/dataverse.js +0 -94
- package/examples/sharePoint Demo/dist/environmentVar.js +0 -55
- package/examples/sharePoint Demo/dist/office365groups.js +0 -97
- package/examples/sharePoint Demo/dist/office365users.js +0 -169
- package/examples/sharePoint Demo/dist/outlook.js +0 -162
- package/examples/sharePoint Demo/dist/sharepoint.js +0 -339
|
@@ -1,184 +1,184 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
-
<title>My Profile</title>
|
|
7
|
-
<script type="importmap">
|
|
8
|
-
{
|
|
9
|
-
"imports": {
|
|
10
|
-
"@microsoft/power-apps/data": "./power-apps-data.js"
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
</script>
|
|
14
|
-
<style>
|
|
15
|
-
:root {
|
|
16
|
-
--bg: #f4f5f7;
|
|
17
|
-
--surface: #ffffff;
|
|
18
|
-
--accent: #4f46e5;
|
|
19
|
-
--accent-light: #e0e7ff;
|
|
20
|
-
--text: #1e1e2e;
|
|
21
|
-
--text-muted: #6b7280;
|
|
22
|
-
--border: #e5e7eb;
|
|
23
|
-
--radius: 16px;
|
|
24
|
-
--shadow: 0 1px 3px rgba(0,0,0,.06), 0 1px 2px rgba(0,0,0,.04);
|
|
25
|
-
--shadow-lg: 0 10px 30px rgba(0,0,0,.08);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
29
|
-
|
|
30
|
-
body {
|
|
31
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
32
|
-
background: var(--bg);
|
|
33
|
-
color: var(--text);
|
|
34
|
-
min-height: 100vh;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/* ── Loading state ── */
|
|
38
|
-
.loader {
|
|
39
|
-
display: flex;
|
|
40
|
-
align-items: center;
|
|
41
|
-
justify-content: center;
|
|
42
|
-
height: 100vh;
|
|
43
|
-
gap: 6px;
|
|
44
|
-
}
|
|
45
|
-
.loader span {
|
|
46
|
-
width: 8px; height: 8px;
|
|
47
|
-
border-radius: 50%;
|
|
48
|
-
background: var(--accent);
|
|
49
|
-
animation: pulse .8s ease-in-out infinite alternate;
|
|
50
|
-
}
|
|
51
|
-
.loader span:nth-child(2) { animation-delay: .15s; }
|
|
52
|
-
.loader span:nth-child(3) { animation-delay: .3s; }
|
|
53
|
-
@keyframes pulse { to { opacity: .2; transform: scale(.7); } }
|
|
54
|
-
|
|
55
|
-
/* ── Layout ── */
|
|
56
|
-
.app { max-width: 960px; margin: 0 auto; padding: 32px 20px 48px; }
|
|
57
|
-
|
|
58
|
-
/* ── Hero card ── */
|
|
59
|
-
.hero {
|
|
60
|
-
position: relative;
|
|
61
|
-
background: var(--surface);
|
|
62
|
-
border-radius: var(--radius);
|
|
63
|
-
box-shadow: var(--shadow-lg);
|
|
64
|
-
overflow: hidden;
|
|
65
|
-
margin-bottom: 28px;
|
|
66
|
-
}
|
|
67
|
-
.hero-banner {
|
|
68
|
-
height: 120px;
|
|
69
|
-
background: linear-gradient(135deg, var(--accent), #7c3aed, #a855f7);
|
|
70
|
-
}
|
|
71
|
-
.hero-body {
|
|
72
|
-
display: flex;
|
|
73
|
-
align-items: flex-end;
|
|
74
|
-
gap: 20px;
|
|
75
|
-
padding: 0 28px 24px;
|
|
76
|
-
margin-top: -52px;
|
|
77
|
-
position: relative;
|
|
78
|
-
}
|
|
79
|
-
.avatar-ring {
|
|
80
|
-
flex-shrink: 0;
|
|
81
|
-
width: 104px; height: 104px;
|
|
82
|
-
border-radius: 50%;
|
|
83
|
-
border: 4px solid var(--surface);
|
|
84
|
-
box-shadow: var(--shadow);
|
|
85
|
-
overflow: hidden;
|
|
86
|
-
background: var(--accent-light);
|
|
87
|
-
display: flex;
|
|
88
|
-
align-items: center;
|
|
89
|
-
justify-content: center;
|
|
90
|
-
}
|
|
91
|
-
.avatar-ring img {
|
|
92
|
-
width: 100%; height: 100%;
|
|
93
|
-
object-fit: cover;
|
|
94
|
-
}
|
|
95
|
-
.avatar-ring .initials {
|
|
96
|
-
font-size: 28px;
|
|
97
|
-
font-weight: 700;
|
|
98
|
-
color: var(--accent);
|
|
99
|
-
}
|
|
100
|
-
.hero-info { padding-bottom: 4px; }
|
|
101
|
-
.hero-info h1 { font-size: 22px; font-weight: 700; line-height: 1.2; }
|
|
102
|
-
.hero-info p { color: var(--text-muted); font-size: 14px; margin-top: 2px; }
|
|
103
|
-
|
|
104
|
-
/* ── Detail chips ── */
|
|
105
|
-
.details {
|
|
106
|
-
display: flex;
|
|
107
|
-
flex-wrap: wrap;
|
|
108
|
-
gap: 10px;
|
|
109
|
-
padding: 0 28px 24px;
|
|
110
|
-
}
|
|
111
|
-
.chip {
|
|
112
|
-
display: inline-flex;
|
|
113
|
-
align-items: center;
|
|
114
|
-
gap: 6px;
|
|
115
|
-
background: var(--bg);
|
|
116
|
-
border: 1px solid var(--border);
|
|
117
|
-
border-radius: 999px;
|
|
118
|
-
padding: 6px 14px;
|
|
119
|
-
font-size: 13px;
|
|
120
|
-
color: var(--text);
|
|
121
|
-
}
|
|
122
|
-
.chip svg { width: 14px; height: 14px; color: var(--text-muted); flex-shrink: 0; }
|
|
123
|
-
|
|
124
|
-
/* ── Section titles ── */
|
|
125
|
-
.section-title {
|
|
126
|
-
font-size: 13px;
|
|
127
|
-
font-weight: 600;
|
|
128
|
-
text-transform: uppercase;
|
|
129
|
-
letter-spacing: .06em;
|
|
130
|
-
color: var(--text-muted);
|
|
131
|
-
margin-bottom: 12px;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/* ── Columns ── */
|
|
135
|
-
.columns { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
|
|
136
|
-
@media (max-width: 640px) { .columns { grid-template-columns: 1fr; } }
|
|
137
|
-
|
|
138
|
-
/* ── Person card ── */
|
|
139
|
-
.card {
|
|
140
|
-
background: var(--surface);
|
|
141
|
-
border-radius: var(--radius);
|
|
142
|
-
box-shadow: var(--shadow);
|
|
143
|
-
padding: 20px;
|
|
144
|
-
}
|
|
145
|
-
.person-row {
|
|
146
|
-
display: flex;
|
|
147
|
-
align-items: center;
|
|
148
|
-
gap: 14px;
|
|
149
|
-
padding: 10px 0;
|
|
150
|
-
}
|
|
151
|
-
.person-row + .person-row { border-top: 1px solid var(--border); }
|
|
152
|
-
.person-avatar {
|
|
153
|
-
width: 40px; height: 40px;
|
|
154
|
-
border-radius: 50%;
|
|
155
|
-
background: var(--accent-light);
|
|
156
|
-
display: flex;
|
|
157
|
-
align-items: center;
|
|
158
|
-
justify-content: center;
|
|
159
|
-
flex-shrink: 0;
|
|
160
|
-
overflow: hidden;
|
|
161
|
-
font-size: 14px;
|
|
162
|
-
font-weight: 600;
|
|
163
|
-
color: var(--accent);
|
|
164
|
-
}
|
|
165
|
-
.person-avatar img { width: 100%; height: 100%; object-fit: cover; }
|
|
166
|
-
.person-meta { min-width: 0; }
|
|
167
|
-
.person-name { font-size: 14px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
168
|
-
.person-title { font-size: 12px; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
169
|
-
|
|
170
|
-
.empty-state {
|
|
171
|
-
text-align: center;
|
|
172
|
-
padding: 24px 0;
|
|
173
|
-
color: var(--text-muted);
|
|
174
|
-
font-size: 13px;
|
|
175
|
-
}
|
|
176
|
-
</style>
|
|
177
|
-
</head>
|
|
178
|
-
<body>
|
|
179
|
-
<div id="root">
|
|
180
|
-
<div class="loader"><span></span><span></span><span></span></div>
|
|
181
|
-
</div>
|
|
182
|
-
<script type="module" src="index.js"></script>
|
|
183
|
-
</body>
|
|
184
|
-
</html>
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>My Profile</title>
|
|
7
|
+
<script type="importmap">
|
|
8
|
+
{
|
|
9
|
+
"imports": {
|
|
10
|
+
"@microsoft/power-apps/data": "./power-apps-data.js"
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
</script>
|
|
14
|
+
<style>
|
|
15
|
+
:root {
|
|
16
|
+
--bg: #f4f5f7;
|
|
17
|
+
--surface: #ffffff;
|
|
18
|
+
--accent: #4f46e5;
|
|
19
|
+
--accent-light: #e0e7ff;
|
|
20
|
+
--text: #1e1e2e;
|
|
21
|
+
--text-muted: #6b7280;
|
|
22
|
+
--border: #e5e7eb;
|
|
23
|
+
--radius: 16px;
|
|
24
|
+
--shadow: 0 1px 3px rgba(0,0,0,.06), 0 1px 2px rgba(0,0,0,.04);
|
|
25
|
+
--shadow-lg: 0 10px 30px rgba(0,0,0,.08);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
29
|
+
|
|
30
|
+
body {
|
|
31
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
32
|
+
background: var(--bg);
|
|
33
|
+
color: var(--text);
|
|
34
|
+
min-height: 100vh;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/* ── Loading state ── */
|
|
38
|
+
.loader {
|
|
39
|
+
display: flex;
|
|
40
|
+
align-items: center;
|
|
41
|
+
justify-content: center;
|
|
42
|
+
height: 100vh;
|
|
43
|
+
gap: 6px;
|
|
44
|
+
}
|
|
45
|
+
.loader span {
|
|
46
|
+
width: 8px; height: 8px;
|
|
47
|
+
border-radius: 50%;
|
|
48
|
+
background: var(--accent);
|
|
49
|
+
animation: pulse .8s ease-in-out infinite alternate;
|
|
50
|
+
}
|
|
51
|
+
.loader span:nth-child(2) { animation-delay: .15s; }
|
|
52
|
+
.loader span:nth-child(3) { animation-delay: .3s; }
|
|
53
|
+
@keyframes pulse { to { opacity: .2; transform: scale(.7); } }
|
|
54
|
+
|
|
55
|
+
/* ── Layout ── */
|
|
56
|
+
.app { max-width: 960px; margin: 0 auto; padding: 32px 20px 48px; }
|
|
57
|
+
|
|
58
|
+
/* ── Hero card ── */
|
|
59
|
+
.hero {
|
|
60
|
+
position: relative;
|
|
61
|
+
background: var(--surface);
|
|
62
|
+
border-radius: var(--radius);
|
|
63
|
+
box-shadow: var(--shadow-lg);
|
|
64
|
+
overflow: hidden;
|
|
65
|
+
margin-bottom: 28px;
|
|
66
|
+
}
|
|
67
|
+
.hero-banner {
|
|
68
|
+
height: 120px;
|
|
69
|
+
background: linear-gradient(135deg, var(--accent), #7c3aed, #a855f7);
|
|
70
|
+
}
|
|
71
|
+
.hero-body {
|
|
72
|
+
display: flex;
|
|
73
|
+
align-items: flex-end;
|
|
74
|
+
gap: 20px;
|
|
75
|
+
padding: 0 28px 24px;
|
|
76
|
+
margin-top: -52px;
|
|
77
|
+
position: relative;
|
|
78
|
+
}
|
|
79
|
+
.avatar-ring {
|
|
80
|
+
flex-shrink: 0;
|
|
81
|
+
width: 104px; height: 104px;
|
|
82
|
+
border-radius: 50%;
|
|
83
|
+
border: 4px solid var(--surface);
|
|
84
|
+
box-shadow: var(--shadow);
|
|
85
|
+
overflow: hidden;
|
|
86
|
+
background: var(--accent-light);
|
|
87
|
+
display: flex;
|
|
88
|
+
align-items: center;
|
|
89
|
+
justify-content: center;
|
|
90
|
+
}
|
|
91
|
+
.avatar-ring img {
|
|
92
|
+
width: 100%; height: 100%;
|
|
93
|
+
object-fit: cover;
|
|
94
|
+
}
|
|
95
|
+
.avatar-ring .initials {
|
|
96
|
+
font-size: 28px;
|
|
97
|
+
font-weight: 700;
|
|
98
|
+
color: var(--accent);
|
|
99
|
+
}
|
|
100
|
+
.hero-info { padding-bottom: 4px; }
|
|
101
|
+
.hero-info h1 { font-size: 22px; font-weight: 700; line-height: 1.2; }
|
|
102
|
+
.hero-info p { color: var(--text-muted); font-size: 14px; margin-top: 2px; }
|
|
103
|
+
|
|
104
|
+
/* ── Detail chips ── */
|
|
105
|
+
.details {
|
|
106
|
+
display: flex;
|
|
107
|
+
flex-wrap: wrap;
|
|
108
|
+
gap: 10px;
|
|
109
|
+
padding: 0 28px 24px;
|
|
110
|
+
}
|
|
111
|
+
.chip {
|
|
112
|
+
display: inline-flex;
|
|
113
|
+
align-items: center;
|
|
114
|
+
gap: 6px;
|
|
115
|
+
background: var(--bg);
|
|
116
|
+
border: 1px solid var(--border);
|
|
117
|
+
border-radius: 999px;
|
|
118
|
+
padding: 6px 14px;
|
|
119
|
+
font-size: 13px;
|
|
120
|
+
color: var(--text);
|
|
121
|
+
}
|
|
122
|
+
.chip svg { width: 14px; height: 14px; color: var(--text-muted); flex-shrink: 0; }
|
|
123
|
+
|
|
124
|
+
/* ── Section titles ── */
|
|
125
|
+
.section-title {
|
|
126
|
+
font-size: 13px;
|
|
127
|
+
font-weight: 600;
|
|
128
|
+
text-transform: uppercase;
|
|
129
|
+
letter-spacing: .06em;
|
|
130
|
+
color: var(--text-muted);
|
|
131
|
+
margin-bottom: 12px;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/* ── Columns ── */
|
|
135
|
+
.columns { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
|
|
136
|
+
@media (max-width: 640px) { .columns { grid-template-columns: 1fr; } }
|
|
137
|
+
|
|
138
|
+
/* ── Person card ── */
|
|
139
|
+
.card {
|
|
140
|
+
background: var(--surface);
|
|
141
|
+
border-radius: var(--radius);
|
|
142
|
+
box-shadow: var(--shadow);
|
|
143
|
+
padding: 20px;
|
|
144
|
+
}
|
|
145
|
+
.person-row {
|
|
146
|
+
display: flex;
|
|
147
|
+
align-items: center;
|
|
148
|
+
gap: 14px;
|
|
149
|
+
padding: 10px 0;
|
|
150
|
+
}
|
|
151
|
+
.person-row + .person-row { border-top: 1px solid var(--border); }
|
|
152
|
+
.person-avatar {
|
|
153
|
+
width: 40px; height: 40px;
|
|
154
|
+
border-radius: 50%;
|
|
155
|
+
background: var(--accent-light);
|
|
156
|
+
display: flex;
|
|
157
|
+
align-items: center;
|
|
158
|
+
justify-content: center;
|
|
159
|
+
flex-shrink: 0;
|
|
160
|
+
overflow: hidden;
|
|
161
|
+
font-size: 14px;
|
|
162
|
+
font-weight: 600;
|
|
163
|
+
color: var(--accent);
|
|
164
|
+
}
|
|
165
|
+
.person-avatar img { width: 100%; height: 100%; object-fit: cover; }
|
|
166
|
+
.person-meta { min-width: 0; }
|
|
167
|
+
.person-name { font-size: 14px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
168
|
+
.person-title { font-size: 12px; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
169
|
+
|
|
170
|
+
.empty-state {
|
|
171
|
+
text-align: center;
|
|
172
|
+
padding: 24px 0;
|
|
173
|
+
color: var(--text-muted);
|
|
174
|
+
font-size: 13px;
|
|
175
|
+
}
|
|
176
|
+
</style>
|
|
177
|
+
</head>
|
|
178
|
+
<body>
|
|
179
|
+
<div id="root">
|
|
180
|
+
<div class="loader"><span></span><span></span><span></span></div>
|
|
181
|
+
</div>
|
|
182
|
+
<script type="module" src="index.js"></script>
|
|
183
|
+
</body>
|
|
184
|
+
</html>
|
|
@@ -1,142 +1,142 @@
|
|
|
1
|
-
import { getMyProfile, getUserPhoto, getManager, getDirectReports } from "./office365users.js";
|
|
2
|
-
|
|
3
|
-
const root = document.getElementById("root");
|
|
4
|
-
|
|
5
|
-
// ── SVG icons ──────────────────────────────────────────────────
|
|
6
|
-
const icons = {
|
|
7
|
-
mail: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"><path d="M3 4a2 2 0 00-2 2v1.161l8.441 4.221a1.25 1.25 0 001.118 0L19 7.162V6a2 2 0 00-2-2H3z"/><path d="M19 8.839l-7.77 3.885a2.75 2.75 0 01-2.46 0L1 8.839V14a2 2 0 002 2h14a2 2 0 002-2V8.839z"/></svg>`,
|
|
8
|
-
phone: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M2 3.5A1.5 1.5 0 013.5 2h1.148a1.5 1.5 0 011.465 1.175l.716 3.223a1.5 1.5 0 01-.638 1.59l-.558.372a.5.5 0 00-.183.548c.4 1.347 1.394 2.34 2.74 2.74a.5.5 0 00.548-.183l.372-.558a1.5 1.5 0 011.59-.638l3.223.716A1.5 1.5 0 0118 12.352v1.148A1.5 1.5 0 0116.5 15h-1.5A11.5 11.5 0 012 3.5z" clip-rule="evenodd"/></svg>`,
|
|
9
|
-
office: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M4 16.5v-13h-.25a.75.75 0 010-1.5h12.5a.75.75 0 010 1.5H16v13h.25a.75.75 0 010 1.5H3.75a.75.75 0 010-1.5H4zm3-11a.5.5 0 01.5-.5h1a.5.5 0 01.5.5v1a.5.5 0 01-.5.5h-1a.5.5 0 01-.5-.5v-1zm.5 2.5a.5.5 0 00-.5.5v1a.5.5 0 00.5.5h1a.5.5 0 00.5-.5v-1a.5.5 0 00-.5-.5h-1zm-.5 3.5a.5.5 0 01.5-.5h1a.5.5 0 01.5.5v1a.5.5 0 01-.5.5h-1a.5.5 0 01-.5-.5v-1zm3.5-6.5a.5.5 0 00-.5.5v1a.5.5 0 00.5.5h1a.5.5 0 00.5-.5v-1a.5.5 0 00-.5-.5h-1zm-.5 3.5a.5.5 0 01.5-.5h1a.5.5 0 01.5.5v1a.5.5 0 01-.5.5h-1a.5.5 0 01-.5-.5v-1zm.5 2.5a.5.5 0 00-.5.5v1a.5.5 0 00.5.5h1a.5.5 0 00.5-.5v-1a.5.5 0 00-.5-.5h-1z" clip-rule="evenodd"/></svg>`,
|
|
10
|
-
dept: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"><path d="M7 8a3 3 0 100-6 3 3 0 000 6zm7.5 1a2.5 2.5 0 100-5 2.5 2.5 0 000 5zM1.615 16.428a1.224 1.224 0 01-.569-1.175 6.002 6.002 0 0111.908 0c.058.467-.172.92-.57 1.174A9.953 9.953 0 017 18a9.953 9.953 0 01-5.385-1.572zM14.5 16h-.106c.07-.297.088-.611.048-.933a7.47 7.47 0 00-1.588-3.755 4.502 4.502 0 015.874 2.636.818.818 0 01-.36.98A7.465 7.465 0 0114.5 16z"/></svg>`,
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
// ── Helpers ────────────────────────────────────────────────────
|
|
14
|
-
function getInitials(name) {
|
|
15
|
-
if (!name) return "?";
|
|
16
|
-
return name.split(" ").map(w => w[0]).join("").toUpperCase().slice(0, 2);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function renderChip(icon, text) {
|
|
20
|
-
if (!text) return "";
|
|
21
|
-
return `<span class="chip">${icon}${escapeHtml(text)}</span>`;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function escapeHtml(str) {
|
|
25
|
-
const d = document.createElement("div");
|
|
26
|
-
d.textContent = str;
|
|
27
|
-
return d.innerHTML;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function personRow(name, title, photoData) {
|
|
31
|
-
const avatarContent = photoData
|
|
32
|
-
? `<img src="data:image/jpeg;base64,${photoData}" alt="">`
|
|
33
|
-
: `<span>${getInitials(name)}</span>`;
|
|
34
|
-
return `
|
|
35
|
-
<div class="person-row">
|
|
36
|
-
<div class="person-avatar">${avatarContent}</div>
|
|
37
|
-
<div class="person-meta">
|
|
38
|
-
<div class="person-name">${escapeHtml(name || "Unknown")}</div>
|
|
39
|
-
<div class="person-title">${escapeHtml(title || "")}</div>
|
|
40
|
-
</div>
|
|
41
|
-
</div>`;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// ── Load photo (returns base64 or null) ────────────────────────
|
|
45
|
-
async function loadPhoto(userId) {
|
|
46
|
-
try {
|
|
47
|
-
const res = await getUserPhoto(userId);
|
|
48
|
-
return res?.value || res || null;
|
|
49
|
-
} catch { return null; }
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// ── Boot ───────────────────────────────────────────────────────
|
|
53
|
-
async function boot() {
|
|
54
|
-
try {
|
|
55
|
-
// 1. Fetch profile
|
|
56
|
-
const me = await getMyProfile();
|
|
57
|
-
const userId = me.id || me.mail || me.userPrincipalName;
|
|
58
|
-
|
|
59
|
-
// 2. Fetch photo, manager, and direct reports in parallel
|
|
60
|
-
const [photo, manager, reports] = await Promise.all([
|
|
61
|
-
loadPhoto(userId),
|
|
62
|
-
getManager(userId).catch(() => null),
|
|
63
|
-
getDirectReports(userId).catch(() => ({ value: [] })),
|
|
64
|
-
]);
|
|
65
|
-
|
|
66
|
-
// 3. Load manager photo
|
|
67
|
-
const mgrId = manager?.id || manager?.mail || manager?.userPrincipalName;
|
|
68
|
-
const mgrPhoto = mgrId ? await loadPhoto(mgrId) : null;
|
|
69
|
-
|
|
70
|
-
// 4. Load report photos in parallel
|
|
71
|
-
const reportList = reports?.value || [];
|
|
72
|
-
const reportPhotos = await Promise.all(
|
|
73
|
-
reportList.map(r => loadPhoto(r.id || r.mail || r.userPrincipalName))
|
|
74
|
-
);
|
|
75
|
-
|
|
76
|
-
// 5. Render
|
|
77
|
-
render(me, photo, manager, mgrPhoto, reportList, reportPhotos);
|
|
78
|
-
} catch (err) {
|
|
79
|
-
root.innerHTML = `<div class="app"><div class="card" style="text-align:center;padding:40px;color:var(--text-muted)">
|
|
80
|
-
<p style="font-size:15px;font-weight:600;margin-bottom:8px">Unable to load profile</p>
|
|
81
|
-
<p style="font-size:13px">${escapeHtml(String(err))}</p>
|
|
82
|
-
</div></div>`;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function render(me, photo, manager, mgrPhoto, reports, reportPhotos) {
|
|
87
|
-
const avatarContent = photo
|
|
88
|
-
? `<img src="data:image/jpeg;base64,${photo}" alt="Profile photo">`
|
|
89
|
-
: `<span class="initials">${getInitials(me.displayName)}</span>`;
|
|
90
|
-
|
|
91
|
-
const chips = [
|
|
92
|
-
renderChip(icons.mail, me.mail),
|
|
93
|
-
renderChip(icons.phone, me.businessPhones?.[0] || me.mobilePhone),
|
|
94
|
-
renderChip(icons.office, me.officeLocation),
|
|
95
|
-
renderChip(icons.dept, me.department),
|
|
96
|
-
].filter(Boolean).join("");
|
|
97
|
-
|
|
98
|
-
// Manager section
|
|
99
|
-
const managerHtml = manager
|
|
100
|
-
? personRow(manager.displayName, manager.jobTitle, mgrPhoto)
|
|
101
|
-
: `<div class="empty-state">No manager found</div>`;
|
|
102
|
-
|
|
103
|
-
// Direct reports section
|
|
104
|
-
let reportsHtml;
|
|
105
|
-
if (reports.length === 0) {
|
|
106
|
-
reportsHtml = `<div class="empty-state">No direct reports</div>`;
|
|
107
|
-
} else {
|
|
108
|
-
reportsHtml = reports
|
|
109
|
-
.map((r, i) => personRow(r.displayName, r.jobTitle, reportPhotos[i]))
|
|
110
|
-
.join("");
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
root.innerHTML = `
|
|
114
|
-
<div class="app">
|
|
115
|
-
<!-- Hero -->
|
|
116
|
-
<div class="hero">
|
|
117
|
-
<div class="hero-banner"></div>
|
|
118
|
-
<div class="hero-body">
|
|
119
|
-
<div class="avatar-ring">${avatarContent}</div>
|
|
120
|
-
<div class="hero-info">
|
|
121
|
-
<h1>${escapeHtml(me.displayName || "")}</h1>
|
|
122
|
-
<p>${escapeHtml(me.jobTitle || "")}</p>
|
|
123
|
-
</div>
|
|
124
|
-
</div>
|
|
125
|
-
<div class="details">${chips}</div>
|
|
126
|
-
</div>
|
|
127
|
-
|
|
128
|
-
<!-- Manager & Reports -->
|
|
129
|
-
<div class="columns">
|
|
130
|
-
<div>
|
|
131
|
-
<div class="section-title">Manager</div>
|
|
132
|
-
<div class="card">${managerHtml}</div>
|
|
133
|
-
</div>
|
|
134
|
-
<div>
|
|
135
|
-
<div class="section-title">Direct Reports</div>
|
|
136
|
-
<div class="card">${reportsHtml}</div>
|
|
137
|
-
</div>
|
|
138
|
-
</div>
|
|
139
|
-
</div>`;
|
|
140
|
-
}
|
|
141
|
-
|
|
1
|
+
import { getMyProfile, getUserPhoto, getManager, getDirectReports } from "./office365users.js";
|
|
2
|
+
|
|
3
|
+
const root = document.getElementById("root");
|
|
4
|
+
|
|
5
|
+
// ── SVG icons ──────────────────────────────────────────────────
|
|
6
|
+
const icons = {
|
|
7
|
+
mail: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"><path d="M3 4a2 2 0 00-2 2v1.161l8.441 4.221a1.25 1.25 0 001.118 0L19 7.162V6a2 2 0 00-2-2H3z"/><path d="M19 8.839l-7.77 3.885a2.75 2.75 0 01-2.46 0L1 8.839V14a2 2 0 002 2h14a2 2 0 002-2V8.839z"/></svg>`,
|
|
8
|
+
phone: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M2 3.5A1.5 1.5 0 013.5 2h1.148a1.5 1.5 0 011.465 1.175l.716 3.223a1.5 1.5 0 01-.638 1.59l-.558.372a.5.5 0 00-.183.548c.4 1.347 1.394 2.34 2.74 2.74a.5.5 0 00.548-.183l.372-.558a1.5 1.5 0 011.59-.638l3.223.716A1.5 1.5 0 0118 12.352v1.148A1.5 1.5 0 0116.5 15h-1.5A11.5 11.5 0 012 3.5z" clip-rule="evenodd"/></svg>`,
|
|
9
|
+
office: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M4 16.5v-13h-.25a.75.75 0 010-1.5h12.5a.75.75 0 010 1.5H16v13h.25a.75.75 0 010 1.5H3.75a.75.75 0 010-1.5H4zm3-11a.5.5 0 01.5-.5h1a.5.5 0 01.5.5v1a.5.5 0 01-.5.5h-1a.5.5 0 01-.5-.5v-1zm.5 2.5a.5.5 0 00-.5.5v1a.5.5 0 00.5.5h1a.5.5 0 00.5-.5v-1a.5.5 0 00-.5-.5h-1zm-.5 3.5a.5.5 0 01.5-.5h1a.5.5 0 01.5.5v1a.5.5 0 01-.5.5h-1a.5.5 0 01-.5-.5v-1zm3.5-6.5a.5.5 0 00-.5.5v1a.5.5 0 00.5.5h1a.5.5 0 00.5-.5v-1a.5.5 0 00-.5-.5h-1zm-.5 3.5a.5.5 0 01.5-.5h1a.5.5 0 01.5.5v1a.5.5 0 01-.5.5h-1a.5.5 0 01-.5-.5v-1zm.5 2.5a.5.5 0 00-.5.5v1a.5.5 0 00.5.5h1a.5.5 0 00.5-.5v-1a.5.5 0 00-.5-.5h-1z" clip-rule="evenodd"/></svg>`,
|
|
10
|
+
dept: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"><path d="M7 8a3 3 0 100-6 3 3 0 000 6zm7.5 1a2.5 2.5 0 100-5 2.5 2.5 0 000 5zM1.615 16.428a1.224 1.224 0 01-.569-1.175 6.002 6.002 0 0111.908 0c.058.467-.172.92-.57 1.174A9.953 9.953 0 017 18a9.953 9.953 0 01-5.385-1.572zM14.5 16h-.106c.07-.297.088-.611.048-.933a7.47 7.47 0 00-1.588-3.755 4.502 4.502 0 015.874 2.636.818.818 0 01-.36.98A7.465 7.465 0 0114.5 16z"/></svg>`,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
// ── Helpers ────────────────────────────────────────────────────
|
|
14
|
+
function getInitials(name) {
|
|
15
|
+
if (!name) return "?";
|
|
16
|
+
return name.split(" ").map(w => w[0]).join("").toUpperCase().slice(0, 2);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function renderChip(icon, text) {
|
|
20
|
+
if (!text) return "";
|
|
21
|
+
return `<span class="chip">${icon}${escapeHtml(text)}</span>`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function escapeHtml(str) {
|
|
25
|
+
const d = document.createElement("div");
|
|
26
|
+
d.textContent = str;
|
|
27
|
+
return d.innerHTML;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function personRow(name, title, photoData) {
|
|
31
|
+
const avatarContent = photoData
|
|
32
|
+
? `<img src="data:image/jpeg;base64,${photoData}" alt="">`
|
|
33
|
+
: `<span>${getInitials(name)}</span>`;
|
|
34
|
+
return `
|
|
35
|
+
<div class="person-row">
|
|
36
|
+
<div class="person-avatar">${avatarContent}</div>
|
|
37
|
+
<div class="person-meta">
|
|
38
|
+
<div class="person-name">${escapeHtml(name || "Unknown")}</div>
|
|
39
|
+
<div class="person-title">${escapeHtml(title || "")}</div>
|
|
40
|
+
</div>
|
|
41
|
+
</div>`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// ── Load photo (returns base64 or null) ────────────────────────
|
|
45
|
+
async function loadPhoto(userId) {
|
|
46
|
+
try {
|
|
47
|
+
const res = await getUserPhoto(userId);
|
|
48
|
+
return res?.value || res || null;
|
|
49
|
+
} catch { return null; }
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// ── Boot ───────────────────────────────────────────────────────
|
|
53
|
+
async function boot() {
|
|
54
|
+
try {
|
|
55
|
+
// 1. Fetch profile
|
|
56
|
+
const me = await getMyProfile();
|
|
57
|
+
const userId = me.id || me.mail || me.userPrincipalName;
|
|
58
|
+
|
|
59
|
+
// 2. Fetch photo, manager, and direct reports in parallel
|
|
60
|
+
const [photo, manager, reports] = await Promise.all([
|
|
61
|
+
loadPhoto(userId),
|
|
62
|
+
getManager(userId).catch(() => null),
|
|
63
|
+
getDirectReports(userId).catch(() => ({ value: [] })),
|
|
64
|
+
]);
|
|
65
|
+
|
|
66
|
+
// 3. Load manager photo
|
|
67
|
+
const mgrId = manager?.id || manager?.mail || manager?.userPrincipalName;
|
|
68
|
+
const mgrPhoto = mgrId ? await loadPhoto(mgrId) : null;
|
|
69
|
+
|
|
70
|
+
// 4. Load report photos in parallel
|
|
71
|
+
const reportList = reports?.value || [];
|
|
72
|
+
const reportPhotos = await Promise.all(
|
|
73
|
+
reportList.map(r => loadPhoto(r.id || r.mail || r.userPrincipalName))
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
// 5. Render
|
|
77
|
+
render(me, photo, manager, mgrPhoto, reportList, reportPhotos);
|
|
78
|
+
} catch (err) {
|
|
79
|
+
root.innerHTML = `<div class="app"><div class="card" style="text-align:center;padding:40px;color:var(--text-muted)">
|
|
80
|
+
<p style="font-size:15px;font-weight:600;margin-bottom:8px">Unable to load profile</p>
|
|
81
|
+
<p style="font-size:13px">${escapeHtml(String(err))}</p>
|
|
82
|
+
</div></div>`;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function render(me, photo, manager, mgrPhoto, reports, reportPhotos) {
|
|
87
|
+
const avatarContent = photo
|
|
88
|
+
? `<img src="data:image/jpeg;base64,${photo}" alt="Profile photo">`
|
|
89
|
+
: `<span class="initials">${getInitials(me.displayName)}</span>`;
|
|
90
|
+
|
|
91
|
+
const chips = [
|
|
92
|
+
renderChip(icons.mail, me.mail),
|
|
93
|
+
renderChip(icons.phone, me.businessPhones?.[0] || me.mobilePhone),
|
|
94
|
+
renderChip(icons.office, me.officeLocation),
|
|
95
|
+
renderChip(icons.dept, me.department),
|
|
96
|
+
].filter(Boolean).join("");
|
|
97
|
+
|
|
98
|
+
// Manager section
|
|
99
|
+
const managerHtml = manager
|
|
100
|
+
? personRow(manager.displayName, manager.jobTitle, mgrPhoto)
|
|
101
|
+
: `<div class="empty-state">No manager found</div>`;
|
|
102
|
+
|
|
103
|
+
// Direct reports section
|
|
104
|
+
let reportsHtml;
|
|
105
|
+
if (reports.length === 0) {
|
|
106
|
+
reportsHtml = `<div class="empty-state">No direct reports</div>`;
|
|
107
|
+
} else {
|
|
108
|
+
reportsHtml = reports
|
|
109
|
+
.map((r, i) => personRow(r.displayName, r.jobTitle, reportPhotos[i]))
|
|
110
|
+
.join("");
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
root.innerHTML = `
|
|
114
|
+
<div class="app">
|
|
115
|
+
<!-- Hero -->
|
|
116
|
+
<div class="hero">
|
|
117
|
+
<div class="hero-banner"></div>
|
|
118
|
+
<div class="hero-body">
|
|
119
|
+
<div class="avatar-ring">${avatarContent}</div>
|
|
120
|
+
<div class="hero-info">
|
|
121
|
+
<h1>${escapeHtml(me.displayName || "")}</h1>
|
|
122
|
+
<p>${escapeHtml(me.jobTitle || "")}</p>
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
<div class="details">${chips}</div>
|
|
126
|
+
</div>
|
|
127
|
+
|
|
128
|
+
<!-- Manager & Reports -->
|
|
129
|
+
<div class="columns">
|
|
130
|
+
<div>
|
|
131
|
+
<div class="section-title">Manager</div>
|
|
132
|
+
<div class="card">${managerHtml}</div>
|
|
133
|
+
</div>
|
|
134
|
+
<div>
|
|
135
|
+
<div class="section-title">Direct Reports</div>
|
|
136
|
+
<div class="card">${reportsHtml}</div>
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
</div>`;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
142
|
boot();
|