Layout tweaks and add custom other responses
This commit is contained in:
@@ -113,28 +113,8 @@ const ResponseFeed = ({ responses, title, renderSummary }) => (
|
||||
<ScrollArea className="h-[400px]">
|
||||
<div className="divide-y divide-gray-100 dark:divide-gray-800">
|
||||
{responses.items.map((response) => (
|
||||
<div key={response.token} className="p-4 hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors">
|
||||
<div className="space-y-1.5">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-sm font-medium text-gray-900 dark:text-gray-100">
|
||||
{response.hidden?.name || 'Anonymous'}
|
||||
</span>
|
||||
{response.hidden?.email && (
|
||||
<span className="text-sm text-muted-foreground">
|
||||
({response.hidden.email})
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<time
|
||||
className="text-xs text-muted-foreground"
|
||||
dateTime={response.submitted_at}
|
||||
>
|
||||
{format(new Date(response.submitted_at), "MMM d, h:mm a")}
|
||||
</time>
|
||||
</div>
|
||||
{renderSummary(response)}
|
||||
</div>
|
||||
<div key={response.token} className="p-4">
|
||||
{renderSummary(response)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -152,18 +132,40 @@ const ProductRelevanceFeed = ({ responses }) => (
|
||||
const textAnswer = response.answers?.find(a => a.type === 'text')?.text;
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<Badge
|
||||
className={
|
||||
answer?.boolean ? "bg-green-200 text-green-700" : "bg-red-200 text-red-700"
|
||||
}
|
||||
>
|
||||
{answer?.boolean ? "Yes" : "No"}
|
||||
</Badge>
|
||||
<div className="space-y-1.5">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
{response.hidden?.email ? (
|
||||
<a
|
||||
href={`https://backend.acherryontop.com/search/?search_for=customers&search=${response.hidden.email}`}
|
||||
className="text-sm font-medium text-gray-900 dark:text-gray-100 hover:underline"
|
||||
>
|
||||
{response.hidden?.name || 'Anonymous'}
|
||||
</a>
|
||||
) : (
|
||||
<span className="text-sm font-medium text-gray-900 dark:text-gray-100">
|
||||
{response.hidden?.name || 'Anonymous'}
|
||||
</span>
|
||||
)}
|
||||
<Badge
|
||||
className={
|
||||
answer?.boolean ? "bg-green-200 text-green-700" : "bg-red-200 text-red-700"
|
||||
}
|
||||
>
|
||||
{answer?.boolean ? "Yes" : "No"}
|
||||
</Badge>
|
||||
</div>
|
||||
<time
|
||||
className="text-xs text-muted-foreground"
|
||||
dateTime={response.submitted_at}
|
||||
>
|
||||
{format(new Date(response.submitted_at), "MMM d")}
|
||||
</time>
|
||||
</div>
|
||||
{textAnswer && (
|
||||
<span className="text-sm text-foreground">
|
||||
<div className="text-sm text-muted-foreground">
|
||||
"{textAnswer}"
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
@@ -178,38 +180,58 @@ const WinbackFeed = ({ responses }) => (
|
||||
renderSummary={(response) => {
|
||||
const likelihoodAnswer = response.answers?.find(a => a.type === 'number');
|
||||
const reasonsAnswer = response.answers?.find(a => a.type === 'choices');
|
||||
const otherAnswer = response.answers?.find(a => a.type === 'text' && a.field.ref.includes('other'));
|
||||
const feedbackAnswer = response.answers?.find(a => a.type === 'text' && !a.field.ref.includes('other'));
|
||||
const feedbackAnswer = response.answers?.find(a => a.type === 'text' && a.field.type === 'long_text');
|
||||
|
||||
return (
|
||||
<div className="space-y-1.5">
|
||||
<div className="flex items-center gap-2">
|
||||
<Badge
|
||||
className={
|
||||
likelihoodAnswer?.number === 1 ? "bg-red-200 text-red-700" :
|
||||
likelihoodAnswer?.number === 2 ? "bg-orange-200 text-orange-700" :
|
||||
likelihoodAnswer?.number === 3 ? "bg-yellow-200 text-yellow-700" :
|
||||
likelihoodAnswer?.number === 4 ? "bg-lime-200 text-lime-700" :
|
||||
likelihoodAnswer?.number === 5 ? "bg-green-200 text-green-700" :
|
||||
"bg-gray-200 text-gray-700"
|
||||
}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
{response.hidden?.email ? (
|
||||
<a
|
||||
href={`https://backend.acherryontop.com/search/?search_for=customers&search=${response.hidden.email}`}
|
||||
className="text-sm font-medium text-gray-900 dark:text-gray-100 hover:underline"
|
||||
>
|
||||
{response.hidden?.name || 'Anonymous'}
|
||||
</a>
|
||||
) : (
|
||||
<span className="text-sm font-medium text-gray-900 dark:text-gray-100">
|
||||
{response.hidden?.name || 'Anonymous'}
|
||||
</span>
|
||||
)}
|
||||
<Badge
|
||||
className={
|
||||
likelihoodAnswer?.number === 1 ? "bg-red-200 text-red-700" :
|
||||
likelihoodAnswer?.number === 2 ? "bg-orange-200 text-orange-700" :
|
||||
likelihoodAnswer?.number === 3 ? "bg-yellow-200 text-yellow-700" :
|
||||
likelihoodAnswer?.number === 4 ? "bg-lime-200 text-lime-700" :
|
||||
likelihoodAnswer?.number === 5 ? "bg-green-200 text-green-700" :
|
||||
"bg-gray-200 text-gray-700"
|
||||
}
|
||||
>
|
||||
{likelihoodAnswer?.number}/5
|
||||
</Badge>
|
||||
</div>
|
||||
<time
|
||||
className="text-xs text-muted-foreground"
|
||||
dateTime={response.submitted_at}
|
||||
>
|
||||
{likelihoodAnswer?.number}/5
|
||||
</Badge>
|
||||
{format(new Date(response.submitted_at), "MMM d")}
|
||||
</time>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{(reasonsAnswer?.choices?.labels || []).map((label, idx) => (
|
||||
<Badge key={idx} variant="secondary" className="text-xs">
|
||||
{label}
|
||||
</Badge>
|
||||
))}
|
||||
{reasonsAnswer?.choices?.other && (
|
||||
<Badge variant="outline" className="text-xs">
|
||||
{reasonsAnswer.choices.other}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
{otherAnswer?.text && (
|
||||
<div className="text-sm">
|
||||
<span className="font-medium">Other:</span>{" "}
|
||||
{otherAnswer.text}
|
||||
</div>
|
||||
)}
|
||||
{feedbackAnswer?.text && (
|
||||
<div className="text-sm">
|
||||
<div className="text-sm text-muted-foreground">
|
||||
{feedbackAnswer.text}
|
||||
</div>
|
||||
)}
|
||||
@@ -293,7 +315,7 @@ const TypeformDashboard = () => {
|
||||
? Math.round((likelihoodAnswers.reduce((a, b) => a + b, 0) / likelihoodAnswers.length) * 10) / 10
|
||||
: 0;
|
||||
|
||||
// Get reasons for not ordering
|
||||
// Get reasons for not ordering (only predefined choices)
|
||||
const reasonsMap = new Map();
|
||||
form2Responses.forEach(response => {
|
||||
const reasonsAnswer = response.answers?.find(a => a.type === 'choices');
|
||||
@@ -388,7 +410,7 @@ const TypeformDashboard = () => {
|
||||
|
||||
|
||||
<Card className="bg-white dark:bg-gray-900/60 backdrop-blur-sm">
|
||||
<CardHeader>
|
||||
<CardHeader className="p-6">
|
||||
<div className="flex items-baseline justify-between">
|
||||
<CardTitle className="text-lg font-semibold text-gray-900 dark:text-gray-100">How likely are you to place another order with us?</CardTitle>
|
||||
<span className={`text-2xl font-bold ${
|
||||
@@ -408,20 +430,20 @@ const TypeformDashboard = () => {
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<BarChart
|
||||
data={likelihoodCounts}
|
||||
margin={{ top: 0, right: 0, left: -20, bottom: 0 }}
|
||||
margin={{ top: 0, right: 10, left: -20, bottom: -25 }}
|
||||
>
|
||||
<CartesianGrid strokeDasharray="3 3" className="stroke-muted" />
|
||||
<XAxis
|
||||
dataKey="rating"
|
||||
tickFormatter={(value) => {
|
||||
return value === "1" ? "Not at all likely" : value === "5" ? "Extremely likely" : "";
|
||||
return value === "1" ? "Not at all" : value === "5" ? "Extremely" : "";
|
||||
}}
|
||||
textAnchor="middle"
|
||||
interval={0}
|
||||
height={50}
|
||||
className="text-muted-foreground"
|
||||
className="text-muted-foreground text-xs md:text-sm"
|
||||
/>
|
||||
<YAxis className="text-muted-foreground" />
|
||||
<YAxis className="text-muted-foreground text-xs md:text-sm" />
|
||||
<Tooltip
|
||||
content={({ payload }) => {
|
||||
if (payload && payload.length) {
|
||||
@@ -459,12 +481,12 @@ const TypeformDashboard = () => {
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card className="bg-white dark:bg-gray-900/60 backdrop-blur-sm">
|
||||
<CardHeader>
|
||||
<div className="flex items-baseline justify-between">
|
||||
<CardHeader className="p-6">
|
||||
<div className="flex items-baseline justify-between gap-2">
|
||||
<CardTitle className="text-lg font-semibold text-gray-900 dark:text-gray-100">Were the suggested products in this email relevant to you?</CardTitle>
|
||||
<div className="flex flex-col items-end">
|
||||
<span className="text-2xl font-bold text-green-600 dark:text-green-500">
|
||||
{metrics.productRelevance.yesPercentage}% Positive
|
||||
{metrics.productRelevance.yesPercentage}% Relevant
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -513,7 +535,7 @@ const TypeformDashboard = () => {
|
||||
return null;
|
||||
}}
|
||||
/>
|
||||
<Bar dataKey="yes" stackId="stack" fill="#10b981" radius={[4, 4, 0, 0]}>
|
||||
<Bar dataKey="yes" stackId="stack" fill="#10b981" radius={[0, 0, 0, 0]}>
|
||||
<text
|
||||
x="50%"
|
||||
y="50%"
|
||||
@@ -525,7 +547,7 @@ const TypeformDashboard = () => {
|
||||
{metrics.productRelevance.yesPercentage}%
|
||||
</text>
|
||||
</Bar>
|
||||
<Bar dataKey="no" stackId="stack" fill="#ef4444" radius={[0, 0, 4, 4]} />
|
||||
<Bar dataKey="no" stackId="stack" fill="#ef4444" radius={[0, 0, 0, 0]} />
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
@@ -537,8 +559,8 @@ const TypeformDashboard = () => {
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 xl:grid-cols-12 gap-6">
|
||||
<div className="col-span-4">
|
||||
<div className="grid grid-cols-2 lg:grid-cols-12 gap-6">
|
||||
<div className="col-span-4 lg:col-span-12 xl:col-span-4">
|
||||
<Card className="bg-white dark:bg-gray-900/60 backdrop-blur-sm">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-lg font-semibold text-gray-900 dark:text-gray-100">Reasons for Not Ordering</CardTitle>
|
||||
@@ -568,10 +590,10 @@ const TypeformDashboard = () => {
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div className="col-span-4 lg:col-span-1 xl:col-span-4">
|
||||
<div className="col-span-4 lg:col-span-6 xl:col-span-4">
|
||||
<WinbackFeed responses={formData.form2.responses} />
|
||||
</div>
|
||||
<div className="col-span-4 lg:col-span-1 xl:col-span-4">
|
||||
<div className="col-span-4 lg:col-span-6 xl:col-span-4">
|
||||
<ProductRelevanceFeed responses={formData.form1.responses} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user