https://claude.ai/public/artifacts/1968d1a0-0e53-4b78-8978-6c441b753012
https://claude.ai/chat/8b6609bd-3e84-4767-8510-abfe521ef927?artifactId=remixed-0f4e84bc
import React, { useState } from 'react';
import { Copy, FileText, MessageSquare, Mail, Calendar, Clock, User, Building } from 'lucide-react';
const TRANSLATIONS = {
"en-US": {
"appTitle": "Professional Note Transformer",
"appDescription": "Transform raw notes into polished, professional formats",
"interviewNotes": "Interview Notes",
"meetingNotes": "Meeting Notes",
"input": "Input",
"context": "Context",
"position": "Position",
"candidateName": "Candidate Name",
"interviewType": "Interview Type",
"duration": "Duration",
"meetingTitle": "Meeting Title",
"attendees": "Attendees",
"date": "Date",
"rawNotes": "Raw Notes",
"rawNotesPlaceholder": "Enter your raw notes here...",
"outputFormat": "Output Format",
"evaluationScorecard": "Evaluation Scorecard",
"slackUpdate": "Slack Update",
"emailSummary": "Email Summary",
"googleDoc": "Google Doc",
"transforming": "Transforming...",
"transformNotes": "Transform Notes",
"output": "Output",
"copy": "Copy",
"transformedNotesPlaceholder": "Transformed notes will appear here"
},
/* LOCALE_PLACEHOLDER_START */
"es-ES": {
"appTitle": "Transformador Profesional de Notas",
"appDescription": "Transforma notas sin procesar en formatos profesionales pulidos",
"interviewNotes": "Notas de Entrevista",
"meetingNotes": "Notas de Reunión",
"input": "Entrada",
"context": "Contexto",
"position": "Posición",
"candidateName": "Nombre del Candidato",
"interviewType": "Tipo de Entrevista",
"duration": "Duración",
"meetingTitle": "Título de la Reunión",
"attendees": "Asistentes",
"date": "Fecha",
"rawNotes": "Notas Sin Procesar",
"rawNotesPlaceholder": "Ingresa tus notas sin procesar aquí...",
"outputFormat": "Formato de Salida",
"evaluationScorecard": "Tarjeta de Evaluación",
"slackUpdate": "Actualización de Slack",
"emailSummary": "Resumen de Email",
"googleDoc": "Documento de Google",
"transforming": "Transformando...",
"transformNotes": "Transformar Notas",
"output": "Salida",
"copy": "Copiar",
"transformedNotesPlaceholder": "Las notas transformadas aparecerán aquí"
}
/* LOCALE_PLACEHOLDER_END */
};
const appLocale = '{{APP_LOCALE}}';
const browserLocale = navigator.languages?.[0] || navigator.language || 'en-US';
const findMatchingLocale = (locale) => {
if (TRANSLATIONS[locale]) return locale;
const lang = locale.split('-')[0];
const match = Object.keys(TRANSLATIONS).find(key => key.startsWith(lang + '-'));
return match || 'en-US';
};
const locale = (appLocale !== '{{APP_LOCALE}}') ? findMatchingLocale(appLocale) : findMatchingLocale(browserLocale);
const t = (key) => TRANSLATIONS[locale]?.[key] || TRANSLATIONS['en-US'][key] || key;
const NoteTransformer = () => {
const [noteType, setNoteType] = useState('interview');
const [context, setContext] = useState({
interview: {
position: '',
candidate: '',
interviewType: '',
duration: ''
},
meeting: {
title: '',
attendees: '',
date: '',
duration: ''
}
});
const [rawNotes, setRawNotes] = useState('');
const [finalUseCase, setFinalUseCase] = useState('');
const [output, setOutput] = useState('');
const [isTransforming, setIsTransforming] = useState(false);
const sampleData = {
interview: {
context: {
position: 'Senior Frontend Developer',
candidate: 'John Smith',
interviewType: 'Technical',
duration: '45 mins'
},
notes: `Candidate: John Smith
- Strong React exp (4 yrs)
- Worked w/ TypeScript, Next.js
- q about state mgmt → mentioned Redux, Context API
- Tech challenge: built todo app w/ good structure
- Soft skills: communicates well, asks good qs
- Concerns: limited backend exp
- Salary expectation: 120-130k
- Available: 2 weeks notice
- Team fit: seems collaborative`
},
meeting: {
context: {
title: 'Q1 Planning Meeting',
attendees: 'Team leads, Product Manager',
date: '2025-01-15',
duration: '60 mins'
},
notes: `Agenda: Q1 roadmap planning
- Revenue target: 2M (up 15% from Q4)
- New feature: mobile app launch
- Timeline: design complete by Feb 15, dev by Mar 30
- Resources: need 2 more devs
- Budget: 500k allocated
- Risks: tight timeline, resource constraints
- Next steps: hiring plan, design kickoff
- Follow-up: weekly check-ins starting next Mon`
}
};
const useCaseOptions = {
interview: [
{ value: 'evaluation', label: t('evaluationScorecard'), icon: FileText },
{ value: 'slack', label: t('slackUpdate'), icon: MessageSquare },
{ value: 'email', label: t('emailSummary'), icon: Mail }
],
meeting: [
{ value: 'googledoc', label: t('googleDoc'), icon: FileText },
{ value: 'slack', label: t('slackUpdate'), icon: MessageSquare },
{ value: 'email', label: t('emailSummary'), icon: Mail }
]
};
const contextFields = {
interview: [
{ key: 'position', label: t('position'), icon: Building },
{ key: 'candidate', label: t('candidateName'), icon: User },
{ key: 'interviewType', label: t('interviewType'), icon: Calendar },
{ key: 'duration', label: t('duration'), icon: Clock }
],
meeting: [
{ key: 'title', label: t('meetingTitle'), icon: Building },
{ key: 'attendees', label: t('attendees'), icon: User },
{ key: 'date', label: t('date'), icon: Calendar },
{ key: 'duration', label: t('duration'), icon: Clock }
]
};
const handleContextChange = (field, value) => {
setContext(prev => ({
...prev,
[noteType]: {
...prev[noteType],
[field]: value
}
}));
};
const getTransformationPrompt = () => {
const currentContext = context[noteType];
const contextString = Object.entries(currentContext)
.map(([key, value]) => ${key}: ${value})
.join(', ');
const basePrompt = `Please respond in ${locale} language. Transform the following raw notes into a professional ${finalUseCase} format.
Context: ${contextString}
Raw Notes:
${rawNotes}
Instructions:
- Expand abbreviations naturally (q → questions, w/ → with, exp → experience, etc.)
- Maintain the original meaning and content
- Structure appropriately for ${finalUseCase}
- Use professional language while preserving key details
- Keep the tone appropriate for the intended use case
Format the output as a ${finalUseCase} would appear in a professional setting.`;
return basePrompt;
};
const transformNotes = async () => {
if (!rawNotes.trim()) return;
setIsTransforming(true);
try {
const response = await fetch("https://api.anthropic.com/v1/messages", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "claude-sonnet-4-20250514",
max_tokens: 2000,
messages: [
{ role: "user", content: getTransformationPrompt() }
]
})
});
if (!response.ok) {
throw new Error(`API request failed: ${response.status}`);
}
const data = await response.json();
setOutput(data.content[0].text);
} catch (error) {
console.error("Error transforming notes:", error);
setOutput("Error: Unable to transform notes. Please try again.");
} finally {
setIsTransforming(false);
}
};
const copyToClipboard = async () => {
try {
await navigator.clipboard.writeText(output);
// You could add a toast notification here
} catch (err) {
console.error('Failed to copy:', err);
}
};
return (
<div className="min-h-screen bg-gray-50 p-6">
<div className="max-w-7xl mx-auto">
<div className="mb-8">
<h1 className="text-3xl font-bold text-gray-900 mb-2">{t('appTitle')}</h1>
<p className="text-gray-600">{t('appDescription')}</p>
</div>
{/* Note Type Selector */}
<div className="mb-6">
<div className="bg-gray-100 p-1 rounded-lg inline-flex">
<button
onClick={() => setNoteType('interview')}
className={`px-6 py-2 rounded-md font-medium transition-all duration-200 ${
noteType === 'interview'
? 'bg-white text-blue-600 shadow-sm'
: 'text-gray-600 hover:text-gray-800'
}`}
>
{t('interviewNotes')}
</button>
<button
onClick={() => setNoteType('meeting')}
className={`px-6 py-2 rounded-md font-medium transition-all duration-200 ${
noteType === 'meeting'
? 'bg-white text-blue-600 shadow-sm'
: 'text-gray-600 hover:text-gray-800'
}`}
>
{t('meetingNotes')}
</button>
</div>
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
{/* Input Column */}
<div className="space-y-6">
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
<h2 className="text-xl font-semibold text-gray-900 mb-4">{t('input')}</h2>
{/* Context Fields */}
<div className="space-y-4 mb-6">
<h3 className="text-lg font-medium text-gray-800">{t('context')}</h3>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
{contextFields[noteType].map(field => {
const Icon = field.icon;
return (
<div key={field.key} className="relative">
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<Icon className="h-5 w-5 text-gray-400" />
</div>
<input
type="text"
placeholder={field.label}
value={context[noteType][field.key]}
onChange={(e) => handleContextChange(field.key, e.target.value)}
className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
</div>
);
})}
</div>
</div>
{/* Raw Notes Input */}
<div className="space-y-4">
<h3 className="text-lg font-medium text-gray-800">{t('rawNotes')}</h3>
<textarea
value={rawNotes}
onChange={(e) => setRawNotes(e.target.value)}
placeholder={t('rawNotesPlaceholder')}
rows={12}
className="w-full p-4 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent resize-none"
/>
</div>
{/* Final Use Case Selector */}
<div className="space-y-4">
<h3 className="text-lg font-medium text-gray-800">{t('outputFormat')}</h3>
<div className="flex flex-wrap gap-2">
{useCaseOptions[noteType].map(option => {
const Icon = option.icon;
return (
<button
key={option.value}
onClick={() => setFinalUseCase(option.value)}
className={`flex items-center space-x-2 px-4 py-2 rounded-lg font-medium transition-colors ${
finalUseCase === option.value
? 'bg-blue-600 text-white'
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
}`}
>
<Icon className="h-4 w-4" />
<span>{option.label}</span>
</button>
);
})}
</div>
</div>
{/* Transform Button */}
<button
onClick={transformNotes}
disabled={!rawNotes.trim() || isTransforming}
className="w-full mt-6 bg-blue-600 text-white py-3 px-6 rounded-lg font-medium hover:bg-blue-700 disabled:bg-gray-300 disabled:cursor-not-allowed transition-colors"
>
{isTransforming ? t('transforming') : t('transformNotes')}
</button>
</div>
</div>
{/* Output Column */}
<div className="space-y-6">
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
<div className="flex justify-between items-center mb-4">
<h2 className="text-xl font-semibold text-gray-900">{t('output')}</h2>
{output && (
<button
onClick={copyToClipboard}
className="flex items-center space-x-2 text-blue-600 hover:text-blue-800 font-medium"
>
<Copy className="h-4 w-4" />
<span>{t('copy')}</span>
</button>
)}
</div>
<div className="min-h-[400px] p-4 bg-gray-50 rounded-lg border border-gray-200">
{output ? (
<div className="whitespace-pre-wrap text-gray-800 leading-relaxed">
{output}
</div>
) : (
<div className="text-gray-500 text-center py-20">
<FileText className="h-12 w-12 mx-auto mb-4 text-gray-400" />
<p>{t('transformedNotesPlaceholder')}</p>
</div>
)}
</div>
</div>
</div>
</div>
</div>
</div>
);
};
export default NoteTransformer;