@@ -194,7 +194,7 @@ private function generateManualContent(Conversation $conversation)
194194 $ customerEmail = 'No email ' ;
195195 if ($ conversation ->customer ) {
196196 $ customerName = $ conversation ->customer ->getFullName () ?: 'Unknown Customer ' ;
197- $ customerEmail = $ conversation ->customer ->email ?: 'No email ' ;
197+ $ customerEmail = $ conversation ->customer ->getMainEmail () ?: 'No email ' ;
198198 }
199199
200200 // Generate title
@@ -317,8 +317,8 @@ private function extractConversationText(Conversation $conversation)
317317
318318 foreach ($ threads as $ thread ) {
319319 $ sender = $ thread ->type === \App \Thread::TYPE_CUSTOMER ? 'Customer ' : 'Support ' ;
320- $ body = strip_tags ($ thread ->body );
321- $ body = strlen ($ body ) > 300 ? substr ($ body , 0 , 300 ) . '... ' : $ body ;
320+ $ body = $ this -> extractStructuredContent ($ thread ->body );
321+ $ body = strlen ($ body ) > 800 ? substr ($ body , 0 , 800 ) . '... ' : $ body ; // Increased limit for structured content
322322
323323 $ text .= "[ $ sender]: $ body \n\n" ;
324324 }
@@ -327,13 +327,101 @@ private function extractConversationText(Conversation $conversation)
327327 return $ text ;
328328 }
329329
330+ /**
331+ * Extract structured content from HTML, preserving form field structure
332+ */
333+ private function extractStructuredContent ($ html )
334+ {
335+ // Check if this looks like a structured HTML table form
336+ if (strpos ($ html , '<table ' ) !== false && strpos ($ html , '<strong> ' ) !== false ) {
337+ return $ this ->parseHTMLTable ($ html );
338+ }
339+
340+ // Fall back to regular strip_tags for simple content
341+ return strip_tags ($ html );
342+ }
343+
344+ /**
345+ * Parse HTML table structure to extract form fields
346+ */
347+ private function parseHTMLTable ($ html )
348+ {
349+ try {
350+ $ structured = [];
351+
352+ // Create DOMDocument to parse HTML properly
353+ $ dom = new \DOMDocument ();
354+
355+ // Suppress HTML parsing warnings for malformed HTML
356+ libxml_use_internal_errors (true );
357+ $ dom ->loadHTML ('<?xml encoding="utf-8" ?> ' . $ html , LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD );
358+ libxml_clear_errors ();
359+
360+ // Find all table rows
361+ $ rows = $ dom ->getElementsByTagName ('tr ' );
362+ $ currentField = null ;
363+
364+ foreach ($ rows as $ row ) {
365+ $ cells = $ row ->getElementsByTagName ('td ' );
366+
367+ if ($ cells ->length >= 2 ) {
368+ $ firstCell = trim ($ cells ->item (0 )->textContent );
369+ $ secondCell = trim ($ cells ->item (1 )->textContent );
370+
371+ // Check if first cell contains a field label (has <strong> tag)
372+ $ strongTags = $ cells ->item (0 )->getElementsByTagName ('strong ' );
373+ if ($ strongTags ->length > 0 ) {
374+ $ currentField = trim ($ strongTags ->item (0 )->textContent );
375+ } else if (!empty ($ secondCell ) && !empty ($ currentField ) && $ secondCell !== ' ' ) {
376+ // This is a value row for the current field
377+ $ structured [$ currentField ] = $ secondCell ;
378+ $ currentField = null ;
379+ }
380+ }
381+ }
382+
383+ // Format the structured data
384+ $ formatted = [];
385+ foreach ($ structured as $ field => $ value ) {
386+ if (!empty ($ value ) && $ value !== ' ' ) {
387+ $ formatted [] = "{$ field }: {$ value }" ;
388+ }
389+ }
390+
391+ $ result = implode ("\n" , $ formatted );
392+
393+ // If we got structured data, return it, otherwise fall back to strip_tags
394+ return !empty ($ result ) ? $ result : strip_tags ($ html );
395+
396+ } catch (\Exception $ e ) {
397+ // If HTML parsing fails, fall back to strip_tags
398+ \Helper::log ('github_html_parsing ' , 'HTML parsing failed: ' . $ e ->getMessage ());
399+ return strip_tags ($ html );
400+ }
401+ }
402+
330403 /**
331404 * Build AI prompt for content generation
332405 */
333406 private function buildPrompt ($ conversationText , Conversation $ conversation )
334407 {
335- $ customerName = $ conversation ->customer ? $ conversation ->customer ->getFullName () : 'Unknown Customer ' ;
336- $ customerEmail = $ conversation ->customer ? $ conversation ->customer ->email : 'No email ' ;
408+ $ customerName = 'Unknown Customer ' ;
409+ $ customerEmail = 'No email ' ;
410+
411+ if ($ conversation ->customer ) {
412+ $ customerName = $ conversation ->customer ->getFullName () ?: 'Unknown Customer ' ;
413+ $ customerEmail = $ conversation ->customer ->getMainEmail () ?: 'No email ' ;
414+ } else {
415+ // Try to load customer manually if the relationship didn't work
416+ if (!empty ($ conversation ->customer_id )) {
417+ $ customer = \App \Customer::find ($ conversation ->customer_id );
418+ if ($ customer ) {
419+ $ customerName = $ customer ->getFullName () ?: 'Unknown Customer ' ;
420+ $ customerEmail = $ customer ->getMainEmail () ?: 'No email ' ;
421+ }
422+ }
423+ }
424+
337425 $ conversationUrl = url ("/conversation/ " . $ conversation ->id );
338426 $ status = ucfirst ($ conversation ->getStatusName ());
339427
@@ -342,7 +430,7 @@ private function buildPrompt($conversationText, Conversation $conversation)
342430
343431 if (!empty ($ customPrompt )) {
344432 // Use custom template with variable replacement
345- return str_replace ([
433+ $ prompt = str_replace ([
346434 '{customer_name} ' ,
347435 '{customer_email} ' ,
348436 '{conversation_url} ' ,
@@ -355,10 +443,12 @@ private function buildPrompt($conversationText, Conversation $conversation)
355443 $ status ,
356444 $ conversationText
357445 ], $ customPrompt );
446+
447+ return $ prompt ;
358448 }
359449
360450 // Default prompt template
361- return "Create a GitHub issue from this customer support conversation.
451+ $ prompt = "Create a GitHub issue from this customer support conversation.
362452
363453Customer: $ customerName
364454Customer Email: $ customerEmail
@@ -384,6 +474,8 @@ private function buildPrompt($conversationText, Conversation $conversation)
384474 \"title \": \"Issue title here \",
385475 \"body \": \"Issue body with markdown formatting \"
386476} " ;
477+
478+ return $ prompt ;
387479 }
388480
389481 /**
0 commit comments