feat: Added collapsible PersonalityProfile component to CreatorDetail p…
- "frontend/src/components/PersonalityProfile.tsx" - "frontend/src/api/creators.ts" - "frontend/src/pages/CreatorDetail.tsx" - "frontend/src/App.css" GSD-Task: S06/T03
This commit is contained in:
parent
15299232a8
commit
73736295c1
5 changed files with 327 additions and 1 deletions
|
|
@ -5573,6 +5573,141 @@ a.app-footer__about:hover,
|
|||
animation: pageEnter 250ms ease-out;
|
||||
}
|
||||
|
||||
/* ── Personality Profile ───────────────────────────────────────────────────── */
|
||||
|
||||
.personality-profile {
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.personality-profile__toggle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
background: none;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 8px;
|
||||
padding: 0.75rem 1rem;
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
color: var(--color-text-primary);
|
||||
transition: background 150ms ease;
|
||||
}
|
||||
|
||||
.personality-profile__toggle:hover {
|
||||
background: var(--color-bg-surface-hover);
|
||||
}
|
||||
|
||||
.personality-profile__heading {
|
||||
margin: 0;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.personality-profile__chevron {
|
||||
margin-left: auto;
|
||||
font-size: 0.9rem;
|
||||
transition: transform 250ms ease;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.personality-profile__chevron--open {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.personality-profile__collapse {
|
||||
display: grid;
|
||||
transition: grid-template-rows 300ms ease;
|
||||
}
|
||||
|
||||
.personality-profile__inner {
|
||||
overflow: hidden;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.personality-profile__summary {
|
||||
color: var(--color-text-secondary);
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.6;
|
||||
margin: 1rem 0;
|
||||
padding: 0 0.25rem;
|
||||
}
|
||||
|
||||
.personality-profile__cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
|
||||
gap: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.personality-profile__card {
|
||||
background: var(--color-bg-surface);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 8px;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.personality-profile__card-title {
|
||||
margin: 0 0 0.75rem;
|
||||
font-size: 0.95rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-accent);
|
||||
}
|
||||
|
||||
.personality-profile__dl {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
gap: 0.25rem 0.75rem;
|
||||
margin: 0 0 0.75rem;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.personality-profile__dl dt {
|
||||
color: var(--color-text-muted);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.personality-profile__dl dd {
|
||||
margin: 0;
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
.personality-profile__pill-label {
|
||||
display: block;
|
||||
font-size: 0.8rem;
|
||||
color: var(--color-text-muted);
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.personality-profile__pills {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.375rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.personality-profile__booleans {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.375rem;
|
||||
font-size: 0.85rem;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.personality-profile__bool {
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.personality-profile__bool--yes {
|
||||
color: var(--color-badge-approved-text);
|
||||
}
|
||||
|
||||
.personality-profile__meta {
|
||||
color: var(--color-text-muted);
|
||||
font-size: 0.8rem;
|
||||
padding: 0 0.25rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
/* ── Admin Technique Pages ────────────────────────────────────────────────── */
|
||||
|
||||
.admin-page {
|
||||
|
|
|
|||
|
|
@ -33,6 +33,39 @@ export interface CreatorTechniqueItem {
|
|||
created_at: string;
|
||||
}
|
||||
|
||||
export interface PersonalityVocabulary {
|
||||
signature_phrases: string[];
|
||||
technical_jargon_level: string;
|
||||
filler_words: string[];
|
||||
distinctive_terms: string[];
|
||||
}
|
||||
|
||||
export interface PersonalityTone {
|
||||
formality: string;
|
||||
energy: string;
|
||||
humor_frequency: string;
|
||||
teaching_style: string;
|
||||
descriptors: string[];
|
||||
}
|
||||
|
||||
export interface PersonalityStyleMarkers {
|
||||
explanation_approach: string;
|
||||
uses_analogies: boolean;
|
||||
uses_sound_words: boolean;
|
||||
self_references_frequency: string;
|
||||
audience_engagement: string;
|
||||
}
|
||||
|
||||
export interface PersonalityProfile {
|
||||
vocabulary: PersonalityVocabulary;
|
||||
tone: PersonalityTone;
|
||||
style_markers: PersonalityStyleMarkers;
|
||||
summary: string;
|
||||
extracted_at: string;
|
||||
transcript_sample_size: number;
|
||||
model_used: string;
|
||||
}
|
||||
|
||||
export interface CreatorDetailResponse {
|
||||
id: string;
|
||||
name: string;
|
||||
|
|
@ -52,6 +85,7 @@ export interface CreatorDetailResponse {
|
|||
follower_count: number;
|
||||
techniques: CreatorTechniqueItem[];
|
||||
genre_breakdown: Record<string, number>;
|
||||
personality_profile: PersonalityProfile | null;
|
||||
}
|
||||
|
||||
export interface CreatorListParams {
|
||||
|
|
|
|||
153
frontend/src/components/PersonalityProfile.tsx
Normal file
153
frontend/src/components/PersonalityProfile.tsx
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
/**
|
||||
* Collapsible personality profile section for the CreatorDetail page.
|
||||
*
|
||||
* Shows vocabulary, tone, and style markers extracted from creator transcripts.
|
||||
* Renders nothing if profile is null. Uses CSS grid-template-rows 0fr/1fr
|
||||
* for smooth collapse/expand animation (per KNOWLEDGE.md).
|
||||
*/
|
||||
|
||||
import { useState } from "react";
|
||||
import type { PersonalityProfile as ProfileType } from "../api/creators";
|
||||
|
||||
interface Props {
|
||||
profile: ProfileType | null;
|
||||
}
|
||||
|
||||
export default function PersonalityProfile({ profile }: Props) {
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
|
||||
if (!profile) return null;
|
||||
|
||||
return (
|
||||
<section className="personality-profile">
|
||||
<button
|
||||
className="personality-profile__toggle"
|
||||
onClick={() => setExpanded((v) => !v)}
|
||||
aria-expanded={expanded}
|
||||
>
|
||||
<h2 className="personality-profile__heading">Personality Profile</h2>
|
||||
<span
|
||||
className={`personality-profile__chevron${expanded ? " personality-profile__chevron--open" : ""}`}
|
||||
aria-hidden="true"
|
||||
>
|
||||
▸
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<div
|
||||
className="personality-profile__collapse"
|
||||
style={{
|
||||
gridTemplateRows: expanded ? "1fr" : "0fr",
|
||||
}}
|
||||
>
|
||||
<div className="personality-profile__inner">
|
||||
{/* Summary */}
|
||||
<p className="personality-profile__summary">{profile.summary}</p>
|
||||
|
||||
<div className="personality-profile__cards">
|
||||
{/* Teaching Style */}
|
||||
<div className="personality-profile__card">
|
||||
<h3 className="personality-profile__card-title">Teaching Style</h3>
|
||||
<dl className="personality-profile__dl">
|
||||
<dt>Formality</dt>
|
||||
<dd>{profile.tone.formality}</dd>
|
||||
<dt>Energy</dt>
|
||||
<dd>{profile.tone.energy}</dd>
|
||||
<dt>Teaching style</dt>
|
||||
<dd>{profile.tone.teaching_style}</dd>
|
||||
<dt>Humor</dt>
|
||||
<dd>{profile.tone.humor_frequency}</dd>
|
||||
</dl>
|
||||
{profile.tone.descriptors.length > 0 && (
|
||||
<div className="personality-profile__pills">
|
||||
{profile.tone.descriptors.map((d) => (
|
||||
<span key={d} className="pill pill--tag">
|
||||
{d}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Vocabulary */}
|
||||
<div className="personality-profile__card">
|
||||
<h3 className="personality-profile__card-title">Vocabulary</h3>
|
||||
<dl className="personality-profile__dl">
|
||||
<dt>Jargon level</dt>
|
||||
<dd>{profile.vocabulary.technical_jargon_level}</dd>
|
||||
{profile.vocabulary.filler_words.length > 0 && (
|
||||
<>
|
||||
<dt>Filler words</dt>
|
||||
<dd>{profile.vocabulary.filler_words.join(", ")}</dd>
|
||||
</>
|
||||
)}
|
||||
</dl>
|
||||
{profile.vocabulary.signature_phrases.length > 0 && (
|
||||
<>
|
||||
<span className="personality-profile__pill-label">
|
||||
Signature phrases
|
||||
</span>
|
||||
<div className="personality-profile__pills">
|
||||
{profile.vocabulary.signature_phrases.map((p) => (
|
||||
<span key={p} className="pill pill--tag">
|
||||
"{p}"
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{profile.vocabulary.distinctive_terms.length > 0 && (
|
||||
<>
|
||||
<span className="personality-profile__pill-label">
|
||||
Distinctive terms
|
||||
</span>
|
||||
<div className="personality-profile__pills">
|
||||
{profile.vocabulary.distinctive_terms.map((t) => (
|
||||
<span key={t} className="pill pill--tag">
|
||||
{t}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Style */}
|
||||
<div className="personality-profile__card">
|
||||
<h3 className="personality-profile__card-title">Style</h3>
|
||||
<dl className="personality-profile__dl">
|
||||
<dt>Explanation approach</dt>
|
||||
<dd>{profile.style_markers.explanation_approach}</dd>
|
||||
<dt>Audience engagement</dt>
|
||||
<dd>{profile.style_markers.audience_engagement}</dd>
|
||||
<dt>Self-references</dt>
|
||||
<dd>{profile.style_markers.self_references_frequency}</dd>
|
||||
</dl>
|
||||
<div className="personality-profile__booleans">
|
||||
<span
|
||||
className={`personality-profile__bool${profile.style_markers.uses_analogies ? " personality-profile__bool--yes" : ""}`}
|
||||
>
|
||||
{profile.style_markers.uses_analogies ? "✓" : "✗"} Uses
|
||||
analogies
|
||||
</span>
|
||||
<span
|
||||
className={`personality-profile__bool${profile.style_markers.uses_sound_words ? " personality-profile__bool--yes" : ""}`}
|
||||
>
|
||||
{profile.style_markers.uses_sound_words ? "✓" : "✗"} Uses
|
||||
sound words
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Metadata */}
|
||||
<div className="personality-profile__meta">
|
||||
Based on {profile.transcript_sample_size} transcript
|
||||
{profile.transcript_sample_size !== 1 ? "s" : ""} · extracted{" "}
|
||||
{new Date(profile.extracted_at).toLocaleDateString()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
|
@ -19,6 +19,7 @@ import { useAuth } from "../context/AuthContext";
|
|||
import CreatorAvatar from "../components/CreatorAvatar";
|
||||
import { SocialIcon } from "../components/SocialIcons";
|
||||
import ChatWidget from "../components/ChatWidget";
|
||||
import PersonalityProfile from "../components/PersonalityProfile";
|
||||
import SortDropdown from "../components/SortDropdown";
|
||||
import TagList from "../components/TagList";
|
||||
import { catSlug } from "../utils/catSlug";
|
||||
|
|
@ -393,6 +394,9 @@ export default function CreatorDetail() {
|
|||
)}
|
||||
</div>
|
||||
|
||||
{/* Personality Profile */}
|
||||
<PersonalityProfile profile={creator.personality_profile ?? null} />
|
||||
|
||||
{/* Technique pages */}
|
||||
<section className="creator-techniques">
|
||||
<div className="creator-techniques__header">
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
{"root":["./src/App.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/api/admin-pipeline.ts","./src/api/admin-techniques.ts","./src/api/auth.ts","./src/api/chat.ts","./src/api/client.ts","./src/api/consent.ts","./src/api/creator-dashboard.ts","./src/api/creators.ts","./src/api/follows.ts","./src/api/highlights.ts","./src/api/index.ts","./src/api/reports.ts","./src/api/search.ts","./src/api/stats.ts","./src/api/techniques.ts","./src/api/topics.ts","./src/api/videos.ts","./src/components/AdminDropdown.tsx","./src/components/AppFooter.tsx","./src/components/AudioWaveform.tsx","./src/components/CategoryIcons.tsx","./src/components/ChapterMarkers.tsx","./src/components/ChatWidget.tsx","./src/components/ConfirmModal.tsx","./src/components/CopyLinkButton.tsx","./src/components/CreatorAvatar.tsx","./src/components/ImpersonationBanner.tsx","./src/components/PlayerControls.tsx","./src/components/ProtectedRoute.tsx","./src/components/ReportIssueModal.tsx","./src/components/SearchAutocomplete.tsx","./src/components/SocialIcons.tsx","./src/components/SortDropdown.tsx","./src/components/TableOfContents.tsx","./src/components/TagList.tsx","./src/components/ToggleSwitch.tsx","./src/components/TranscriptSidebar.tsx","./src/components/VideoPlayer.tsx","./src/context/AuthContext.tsx","./src/hooks/useCountUp.ts","./src/hooks/useDocumentTitle.ts","./src/hooks/useMediaSync.ts","./src/hooks/useSortPreference.ts","./src/pages/About.tsx","./src/pages/AdminAuditLog.tsx","./src/pages/AdminPipeline.tsx","./src/pages/AdminReports.tsx","./src/pages/AdminTechniquePages.tsx","./src/pages/AdminUsers.tsx","./src/pages/ChapterReview.tsx","./src/pages/ChatPage.tsx","./src/pages/ConsentDashboard.tsx","./src/pages/CreatorDashboard.tsx","./src/pages/CreatorDetail.tsx","./src/pages/CreatorSettings.tsx","./src/pages/CreatorTiers.tsx","./src/pages/CreatorsBrowse.tsx","./src/pages/HighlightQueue.tsx","./src/pages/Home.tsx","./src/pages/Login.tsx","./src/pages/Register.tsx","./src/pages/SearchResults.tsx","./src/pages/SubTopicPage.tsx","./src/pages/TechniquePage.tsx","./src/pages/TopicsBrowse.tsx","./src/pages/WatchPage.tsx","./src/utils/catSlug.ts","./src/utils/citations.tsx"],"version":"5.6.3"}
|
||||
{"root":["./src/App.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/api/admin-pipeline.ts","./src/api/admin-techniques.ts","./src/api/auth.ts","./src/api/chat.ts","./src/api/client.ts","./src/api/consent.ts","./src/api/creator-dashboard.ts","./src/api/creators.ts","./src/api/follows.ts","./src/api/highlights.ts","./src/api/index.ts","./src/api/reports.ts","./src/api/search.ts","./src/api/stats.ts","./src/api/techniques.ts","./src/api/topics.ts","./src/api/videos.ts","./src/components/AdminDropdown.tsx","./src/components/AppFooter.tsx","./src/components/AudioWaveform.tsx","./src/components/CategoryIcons.tsx","./src/components/ChapterMarkers.tsx","./src/components/ChatWidget.tsx","./src/components/ConfirmModal.tsx","./src/components/CopyLinkButton.tsx","./src/components/CreatorAvatar.tsx","./src/components/ImpersonationBanner.tsx","./src/components/PersonalityProfile.tsx","./src/components/PlayerControls.tsx","./src/components/ProtectedRoute.tsx","./src/components/ReportIssueModal.tsx","./src/components/SearchAutocomplete.tsx","./src/components/SocialIcons.tsx","./src/components/SortDropdown.tsx","./src/components/TableOfContents.tsx","./src/components/TagList.tsx","./src/components/ToggleSwitch.tsx","./src/components/TranscriptSidebar.tsx","./src/components/VideoPlayer.tsx","./src/context/AuthContext.tsx","./src/hooks/useCountUp.ts","./src/hooks/useDocumentTitle.ts","./src/hooks/useMediaSync.ts","./src/hooks/useSortPreference.ts","./src/pages/About.tsx","./src/pages/AdminAuditLog.tsx","./src/pages/AdminPipeline.tsx","./src/pages/AdminReports.tsx","./src/pages/AdminTechniquePages.tsx","./src/pages/AdminUsers.tsx","./src/pages/ChapterReview.tsx","./src/pages/ChatPage.tsx","./src/pages/ConsentDashboard.tsx","./src/pages/CreatorDashboard.tsx","./src/pages/CreatorDetail.tsx","./src/pages/CreatorSettings.tsx","./src/pages/CreatorTiers.tsx","./src/pages/CreatorsBrowse.tsx","./src/pages/HighlightQueue.tsx","./src/pages/Home.tsx","./src/pages/Login.tsx","./src/pages/Register.tsx","./src/pages/SearchResults.tsx","./src/pages/SubTopicPage.tsx","./src/pages/TechniquePage.tsx","./src/pages/TopicsBrowse.tsx","./src/pages/WatchPage.tsx","./src/utils/catSlug.ts","./src/utils/citations.tsx"],"version":"5.6.3"}
|
||||
Loading…
Add table
Reference in a new issue