Skip to content

Commit

Permalink
Merge pull request #18 from aaron5670/feature/search-for-experiments
Browse files Browse the repository at this point in the history
Search for experiments
  • Loading branch information
aaron5670 authored Dec 19, 2022
2 parents d51946f + 2ecc7fa commit 99096ff
Show file tree
Hide file tree
Showing 15 changed files with 375 additions and 102 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
},
"dependencies": {
"@emotion/react": "^11.10.4",
"@mantine/core": "^5.5.5",
"@mantine/hooks": "^5.5.5",
"@mantine/core": "^5.9.5",
"@mantine/hooks": "^5.9.5",
"@plasmohq/storage": "^0.12.2",
"@tabler/icons": "^1.104.0",
"immer": "^9.0.15",
Expand Down
50 changes: 25 additions & 25 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions src/components/FloatingLabelInput.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useState } from 'react';
import { TextInput, createStyles } from '@mantine/core';
import { TextInput, createStyles, Loader } from "@mantine/core";

const useStyles = createStyles((theme, { floating }: { floating: boolean }) => ({
root: {
Expand Down Expand Up @@ -38,7 +38,7 @@ const useStyles = createStyles((theme, { floating }: { floating: boolean }) => (
},
}));

const FloatingLabelInput = ({label, description= '', value, onChange}) => {
const FloatingLabelInput = ({label, description= '', value, onChange, loading = false}) => {
const [focused, setFocused] = useState(false);
const { classes } = useStyles({ floating: value.trim().length !== 0 || focused });

Expand All @@ -53,6 +53,7 @@ const FloatingLabelInput = ({label, description= '', value, onChange}) => {
mt="md"
radius="md"
autoComplete="nope"
rightSection={loading && <Loader size="xs" />}
/>
);
}
Expand Down
60 changes: 33 additions & 27 deletions src/components/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,40 +1,46 @@
import { ActionIcon, Grid, Text } from "@mantine/core";
import { IconAdjustments } from "@tabler/icons";
import { ActionIcon, Group, Text } from "@mantine/core";
import { IconAdjustments, IconSearch, IconX } from "@tabler/icons";
import useStore from "~store/useStore";

interface HeaderProps {
title: string;
description?: string;
}

const Header = ({title, description = ""}: HeaderProps) => {
const {screen, setScreen} = useStore(state => state);

const changeScreen = () => {
if (screen === 'settings') {
setScreen('home');
} else {
setScreen('settings');
}
}
const Header = ({ title, description = "" }: HeaderProps) => {
const { screen, setScreen } = useStore(state => state);

return (
<Grid>
<Grid.Col span={10}>
<Text size="lg" weight={500}>
<>
<Group position="apart">
<Text
variant="gradient"
gradient={{ from: "indigo", to: "cyan", deg: 45 }}
sx={{ fontFamily: "Greycliff CF, sans-serif" }}
ta="center"
fz="xl"
fw={700}
>
{title}
</Text>
<Text size="xs" color="dimmed" mt={3} mb="xl">
{description}
</Text>
</Grid.Col>
<Grid.Col span={2}>
<ActionIcon onClick={changeScreen}>
<IconAdjustments />
</ActionIcon>
</Grid.Col>
</Grid>
)
}
<Group position="right" spacing={1}>
<ActionIcon
onClick={() => setScreen(screen === "search" ? "home" : "search")}
>
{screen === "search" ? (<IconX />) : (<IconSearch />)}
</ActionIcon>
<ActionIcon
onClick={() => setScreen(screen === "settings" ? "home" : "settings")}
>
<IconAdjustments />
</ActionIcon>
</Group>
</Group>
<Text size="xs" color="dimmed" mt="md" mb="xl">
{description}
</Text>
</>
);
};

export default Header;
101 changes: 101 additions & 0 deletions src/components/SearchItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { Box, Button, Card, Collapse, createStyles, Divider, Group, Loader, Text } from "@mantine/core";
import { useEffect, useState } from "react";
import useStore from "~store/useStore";
import { updateLocalStorageValue } from "~handlers/localStorageHandlers";

const useStyles = createStyles(() => ({
card: {
':hover': {
transition: 'background-color 150ms ease',
backgroundColor: '#f0f0f0',
},
},
}));

interface SearchItemProps {
experiment: {
id: string;
name: string;
description: string;
};
}

const SearchItem = ({ experiment }: SearchItemProps) => {
const { classes } = useStyles();
const { localStorageKey, setLocalStorageValue, optimizelyAccessToken } = useStore(state => state);
const [opened, setOpened] = useState(false);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<null | string>(null);
const [experimentData, setExperimentData] = useState(null);

useEffect(() => {
const fetchExperiment = async () => {
if (opened) {
try {
setLoading(true);
const response = await fetch(`https://api.optimizely.com/v2/experiments/${experiment.id}`, {
method: "GET",
headers: {
Authorization: `Bearer ${optimizelyAccessToken}`
}
});
const data = await response.json();
setExperimentData(data);
} catch (error) {
console.error(error);
setError(error.message);
} finally {
setLoading(false);
}
}
};
fetchExperiment();
}, [opened]);

const saveToLocalStorage = (value) => {
setLocalStorageValue(value);
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
updateLocalStorageValue(tabs[0].id, localStorageKey, value);
});
};

return (
<Card
key={experiment.id}
shadow="sm" p="xs" mb="xs" radius="md"
onClick={() => setOpened(true)}
className={classes.card}
style={{ cursor: "pointer" }}
>
<Text weight={500}>{experiment.name}</Text>
<Text size="sm" color="dimmed">
{experiment.description}
</Text>

<Collapse in={opened}>
{loading ? (<Group position="center" my="md"><Loader /></Group>) : (
<Box my="md">
<Divider my="sm" variant="dashed" />
<Text weight={500}>Whitelisted users</Text>
<Button.Group orientation="vertical">
{experimentData?.whitelist.map((item) => (
<Button
variant="default"
key={item.user_id}
onClick={() => saveToLocalStorage(item.user_id)}
>{item.user_id}</Button>
))}
</Button.Group>
</Box>
)}
{error && (
<Text size="sm" mt="md" c="red" ta="center">
{error}
</Text>
)}
</Collapse>
</Card>
);
};

export default SearchItem;
22 changes: 22 additions & 0 deletions src/components/settings/AccessTokenInputField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { PasswordInput } from "@mantine/core";
import useStore from "~store/useStore";

const description = (
<span>Get your access token <a href="https://app.optimizely.com/v2/profile/api"
target="_blank">here</a>.</span>
);

const AccessTokenInputField = () => {
const { setOptimizelyAccessToken, optimizelyAccessToken } = useStore(state => state);

return (
<PasswordInput
label="Optimizely Personal Access Token"
description={description}
value={optimizelyAccessToken}
onChange={(e) => setOptimizelyAccessToken(e.target.value)}
/>
);
};

export default AccessTokenInputField;
28 changes: 28 additions & 0 deletions src/components/settings/DefaultScreenField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { SegmentedControl, Text } from "@mantine/core";
import useStore from "~store/useStore";

const DefaultScreenField = () => {
const { defaultScreen, setDefaultScreen } = useStore(state => state);

return (
<>
<Text size={14}>
Default screen
</Text>
<SegmentedControl
value={defaultScreen}
data={[
{ label: 'Home', value: 'home' },
{ label: 'Search', value: 'search' },
{ label: 'Settings', value: 'settings' },
]}
onChange={setDefaultScreen}
radius="md"
mb="lg"
fullWidth
/>
</>
);
};

export default DefaultScreenField;
Loading

0 comments on commit 99096ff

Please sign in to comment.