Skip to content

Commit 4c03da8

Browse files
Jaissica HoraJaissica Hora
authored andcommitted
style: segregated and enhanced UI components for better UX
1 parent c5b57c1 commit 4c03da8

File tree

12 files changed

+426
-101
lines changed

12 files changed

+426
-101
lines changed
Lines changed: 34 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,35 @@
1-
import React, { useEffect, useState, useCallback } from 'react'
1+
import { useEffect, useState, useCallback } from 'react'
2+
import Identify from './components/features/Identify'
3+
import Login from './components/features/Login'
4+
import Logout from './components/features/Logout'
5+
import ConversionEvent from './components/features/ConversionEvent'
6+
import PageView from './components/features/PageView'
7+
import Audiences from './components/features/Audiences'
8+
import { ConfigProvider } from './contexts/ConfigContext'
9+
import './app.css'
10+
import OutputLog from './components/OutputLog'
211

312
// Pass you API Key here
413
const API_KEY = ''
514
const CONFIG_URL_BASE = 'http://localhost:4000/JS/v2/'
6-
const MOCK_AUDIENCE_BASE = 'http://localhost:4000/v1/'
715

816
export default function App() {
917
const [sdkReady, setSdkReady] = useState(false)
10-
const [logText, setLogText] = useState('')
18+
const [appLogText, setAppLogText] = useState('')
1119

12-
const log = useCallback((...args) => {
13-
setLogText(prev => prev + args.map(x => (typeof x === 'string' ? x : JSON.stringify(x, null, 2))).join(' ') + '\n')
20+
const appLog = useCallback((...args) => {
21+
setAppLogText(prev => prev + args.map(x => (typeof x === 'string' ? x : JSON.stringify(x, null, 2))).join(' ') + '\n')
1422
}, [])
1523

24+
const features = [
25+
<Identify />,
26+
<Login />,
27+
<Logout />,
28+
<ConversionEvent />,
29+
<PageView />,
30+
<Audiences />
31+
]
32+
1633
useEffect(() => {
1734
const script = document.createElement('script')
1835
script.src = '/mparticle.js'
@@ -23,80 +40,25 @@ export default function App() {
2340
isDevelopmentMode: true,
2441
requestConfig: true,
2542
configUrl: CONFIG_URL_BASE,
26-
identityCallback: resp => log('identityCallback', resp && resp.httpCode),
43+
identityCallback: resp => appLog('identityCallback', resp && resp.httpCode),
2744
}
2845
window.mParticle.init(API_KEY, window.mParticle.config)
2946
setSdkReady(true)
30-
log('mParticle init called')
3147
}
32-
script.onerror = () => log('Failed to load mParticle SDK script')
48+
script.onerror = () => appLog('Failed to load mParticle SDK script')
3349
document.body.appendChild(script)
3450
return () => { document.body.removeChild(script) }
35-
}, [log])
36-
37-
const identify = () => {
38-
window.mParticle?.Identity?.identify(
39-
{ userIdentities: { customerid: 'cust-1', email: '[email protected]' } },
40-
resp => log('identify cb', resp)
41-
)
42-
}
43-
44-
const login = () => {
45-
window.mParticle?.Identity?.login(
46-
{ userIdentities: { customerid: 'cust-1' } },
47-
resp => log('login cb', resp)
48-
)
49-
}
50-
51-
const logout = () => {
52-
window.mParticle?.Identity?.logout({}, resp => log('logout cb', resp))
53-
}
54-
55-
const logEvent = () => {
56-
window.mParticle?.logEvent(
57-
'Test Event',
58-
window.mParticle?.EventType?.Navigation,
59-
{ foo: 'bar' },
60-
{ 'Facebook.ClickId': 'override' }
61-
)
62-
log('logEvent sent')
63-
}
64-
65-
const logPageView = () => {
66-
window.mParticle?.logPageView('Home', { title: document.title })
67-
log('pageView sent')
68-
}
69-
70-
const getAudiences = async () => {
71-
const user = window.mParticle?.Identity?.getCurrentUser()
72-
const mpid = user?.getMPID()
73-
if (!mpid) {
74-
log('No current MPID yet')
75-
return
76-
}
77-
try {
78-
const res = await fetch(`${MOCK_AUDIENCE_BASE}${API_KEY}/audience?mpid=${mpid}`)
79-
const json = await res.json()
80-
log('audiences', json)
81-
} catch (e) {
82-
log('audience fetch error', e?.message || e)
83-
}
84-
}
51+
}, [])
8552

8653
return (
87-
<div style={{ fontFamily: 'sans-serif', padding: 16 }}>
88-
<h1>mParticle E2E Test</h1>
89-
<div style={{ display: 'grid', gap: 8, maxWidth: 400 }}>
90-
<button disabled={!sdkReady} onClick={identify}>Identify</button>
91-
<button disabled={!sdkReady} onClick={login}>Login</button>
92-
<button disabled={!sdkReady} onClick={logout}>Logout</button>
93-
<button disabled={!sdkReady} onClick={logEvent}>Log Conversion Event</button>
94-
<button disabled={!sdkReady} onClick={logPageView}>Log Page View</button>
95-
<button disabled={!sdkReady} onClick={getAudiences}>Get Audiences</button>
96-
</div>
97-
<pre style={{ marginTop: 16, background: '#f7f7f7', padding: 12, minHeight: 200, overflow: 'auto' }}>
98-
{logText}
99-
</pre>
100-
</div>
54+
<ConfigProvider apiKey={API_KEY} configUrlBase={CONFIG_URL_BASE}>
55+
<div className="app-root">
56+
<h1>mParticle E2E Test</h1>
57+
<OutputLog title="App Log" logText={appLogText} />
58+
{sdkReady ? features.map((feat) => {
59+
return feat
60+
}) : null}
61+
</div >
62+
</ConfigProvider>
10163
)
10264
}
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
.app-root {
2+
font-family: sans-serif;
3+
padding: 16px;
4+
}
5+
6+
.app-root {
7+
/* Rokt brand theme variables (update hex values to match official brand guide) */
8+
--rokt-primary: #C20075;
9+
--rokt-primary-hover: #480029;
10+
--rokt-text: #000000;
11+
--rokt-bg: #ffffff;
12+
--rokt-muted-bg: #f7f7f7;
13+
--rokt-border: #e5e7eb;
14+
}
15+
16+
.app-root h1 {
17+
color: var(--rokt-primary);
18+
}
19+
20+
.output-log {
21+
margin-top: 16px;
22+
background: var(--rokt-muted-bg);
23+
padding: 12px;
24+
min-height: 200px;
25+
max-height: 320px;
26+
overflow: auto;
27+
width: 100%;
28+
box-sizing: border-box;
29+
}
30+
31+
/* Collapsible feature section */
32+
.feature-section {
33+
border: 1px solid var(--rokt-border);
34+
border-radius: 8px;
35+
background: #fff;
36+
margin-bottom: 16px;
37+
}
38+
39+
.feature-section__header {
40+
display: flex;
41+
align-items: center;
42+
justify-content: space-between;
43+
padding: 8px 12px;
44+
border-bottom: 1px solid var(--rokt-border);
45+
}
46+
47+
.feature-section__title {
48+
margin: 0;
49+
font-size: 16px;
50+
}
51+
52+
.feature-section__toggle {
53+
padding: 6px 10px;
54+
border-radius: 6px;
55+
border: 1px solid var(--rokt-primary);
56+
background: var(--rokt-primary);
57+
color: #fff;
58+
cursor: pointer;
59+
}
60+
61+
.feature-section__content {
62+
padding: 12px;
63+
}
64+
65+
66+
/* Base button styling to align with Rokt branding */
67+
.app-root button {
68+
display: inline-block;
69+
padding: 10px 14px;
70+
border-radius: 8px;
71+
border: 1px solid var(--rokt-primary);
72+
background: var(--rokt-primary);
73+
color: #ffffff;
74+
cursor: pointer;
75+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
76+
transition: background-color 120ms ease, border-color 120ms ease, box-shadow 120ms ease;
77+
}
78+
79+
.app-root button:hover:not(:disabled) {
80+
background: var(--rokt-primary-hover);
81+
border-color: var(--rokt-primary-hover);
82+
}
83+
84+
.app-root button:disabled {
85+
opacity: 0.6;
86+
cursor: not-allowed;
87+
}
88+
89+
.app-root button:focus-visible {
90+
outline: none;
91+
box-shadow: 0 0 0 3px rgba(255, 0, 80, 0.35);
92+
}
93+
94+
.feature-row {
95+
display: grid;
96+
grid-template-columns: minmax(240px, 360px) 1fr;
97+
gap: 16px;
98+
align-items: stretch;
99+
margin-bottom: 16px;
100+
}
101+
102+
.feature-input {
103+
display: flex;
104+
flex-direction: column;
105+
justify-content: center;
106+
gap: 8px;
107+
align-items: center;
108+
}
109+
110+
.feature-output {
111+
width: 100%;
112+
min-width: 0; /* allow content to shrink within grid to avoid overflow */
113+
}
114+
115+
.feature-output .output-log {
116+
width: 100%;
117+
}
118+
119+
@media (max-width: 640px) {
120+
.feature-row {
121+
grid-template-columns: 1fr;
122+
}
123+
}
124+
125+
126+
127+
128+

core-sdk-samples/mparticle-react-web-sample-app/client/src/components/ButtonPanel.jsx

Lines changed: 0 additions & 29 deletions
This file was deleted.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { useState } from 'react'
2+
3+
export default function FeatureSection({ title, children, defaultOpen = false }) {
4+
const [open, setOpen] = useState(defaultOpen)
5+
return (
6+
<section className="feature-section">
7+
<div className="feature-section__header">
8+
<h2 className="feature-section__title">{title}</h2>
9+
<button
10+
type="button"
11+
className="feature-section__toggle"
12+
onClick={() => setOpen(o => !o)}
13+
aria-expanded={open}
14+
>
15+
{open ? 'Hide' : 'Show'}
16+
</button>
17+
</div>
18+
{open ? (
19+
<div className="feature-section__content">
20+
{children}
21+
</div>
22+
) : null}
23+
</section>
24+
)
25+
}
26+
27+
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
const OutputLog = ({ logText }) => {
2+
return (
3+
<pre className="output-log">
4+
{logText}
5+
</pre>
6+
)
7+
}
8+
9+
export default OutputLog
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
2+
3+
import { useState, useCallback } from "react"
4+
import OutputLog from "../OutputLog"
5+
import FeatureSection from "../FeatureSection"
6+
import { useConfig } from "../../contexts/ConfigContext"
7+
8+
const MOCK_AUDIENCE_BASE = 'http://localhost:4000/v1/'
9+
10+
const Audiences = () => {
11+
const [logText, setLogText] = useState('')
12+
const { apiKey } = useConfig()
13+
14+
const log = useCallback((...args) => {
15+
setLogText(prev => prev + args.map(x => (typeof x === 'string' ? x : JSON.stringify(x, null, 2))).join(' ') + '\n')
16+
}, [])
17+
18+
const getAudiences = async () => {
19+
const user = window.mParticle?.Identity?.getCurrentUser()
20+
const mpid = user?.getMPID()
21+
if (!mpid) {
22+
log('No current MPID yet')
23+
return
24+
}
25+
try {
26+
const res = await fetch(`${MOCK_AUDIENCE_BASE}${apiKey}/audience?mpid=${mpid}`)
27+
const json = await res.json()
28+
log('audiences', json)
29+
} catch (e) {
30+
log('audience fetch error', e?.message || e)
31+
}
32+
}
33+
return (
34+
<FeatureSection title="Audiences">
35+
<div className="feature-row">
36+
<div className="feature-input">
37+
<button onClick={getAudiences}>Get Audiences</button>
38+
</div>
39+
<div className="feature-output">
40+
<OutputLog logText={logText} />
41+
</div>
42+
</div>
43+
</FeatureSection>
44+
)
45+
}
46+
47+
export default Audiences

0 commit comments

Comments
 (0)