{"version":3,"file":"main.DH2V8b.js","sources":["theme-157/virtual:theme","theme-157/discourse/connectors/static-page-after-content__success/static-page-after-content__success.gjs","theme-157/virtual:entrypoint:main"],"sourcesContent":["import { getObjectForTheme } from \"discourse/lib/theme-settings-store\";\n\nexport const settings = getObjectForTheme(157);\n\nexport function themePrefix(key) {\n  return `theme_translations.157.${key}`;\n}\n","import Component from \"@glimmer/component\";\nimport { tracked } from \"@glimmer/tracking\";\nimport { action } from \"@ember/object\";\nimport { service } from \"@ember/service\";\nimport { fn } from \"@ember/helper\";\nimport { on } from \"@ember/modifier\";\nimport { eq, lt, gt } from \"truth-helpers\";\nimport { ajax } from \"discourse/lib/ajax\";\nimport { popupAjaxError } from \"discourse/lib/ajax-error\";\nimport { next } from \"@ember/runloop\";\nimport Chart from \"admin/components/chart\";\n\nexport default class ForumSuccessStats extends Component {\n  @service currentUser;\n  @service router;\n  @tracked selectedPeriod = \"month\";\n  @tracked p90ViewMode = \"1day\"; // \"1day\", \"7day\", or \"30day\" for P90 chart only\n  @tracked error = null;\n  @tracked loadingP90 = true;\n  @tracked loadingSLA = true;\n  @tracked loadingCoverage = true;\n  @tracked loadingCommunityVsStaff = true;\n  @tracked loadingSentiment = true;\n  @tracked loadingQueue = true;\n  @tracked loadingActivity = true;\n  @tracked loadingGoals = true;\n  @tracked loadingTTFRBySeverity = true;\n  @tracked showCustomDates = false;\n  @tracked startDate = null;\n  @tracked endDate = null;\n  @tracked chartLabels = [];\n  @tracked p90ChartData = [];\n  @tracked sev1ChartData = [];\n  @tracked sev2ChartData = [];\n  @tracked sev3ChartData = [];\n  @tracked coverageSev1ChartData = [];\n  @tracked coverageSev2ChartData = [];\n  @tracked coverageSev3ChartData = [];\n  @tracked communityVsStaffData_chart = [];\n  @tracked sentimentOverallData = [];\n  @tracked queueBugs = [];\n  @tracked queueSelectedTab = \"all\"; // \"all\", \"sev1\", \"sev2\", \"sev3\"\n  @tracked queueSortOrder = \"oldest\"; // \"oldest\" or \"newest\"\n  @tracked dauChartData = [];\n  @tracked engagedUsersChartData = [];\n  @tracked dauMauRatioChartData = [];\n  @tracked ttfrMetricType = \"p90\"; // \"p90\", \"p50\", or \"average\"\n  \n  // Store raw TTFR data\n  rawP90Data = [];\n  rawP50Data = [];\n  rawAvgData = [];\n  @tracked queryIds = {\n    p90_ttfr: null,\n    sla_compliance: null,\n    coverage: null,\n    community_vs_staff: null,\n    sentiment: null,\n    bug_queue: null,\n    user_activity: null,\n    goals: null,\n    goals_7d: null,\n    ttfr_by_severity: null\n  };\n  \n  // Query result cache (in-memory, not persisted)\n  queryCache = {};\n  \n  // Query generation tracking to prevent stale data\n  currentQueryGeneration = 0;\n\n  // TTFR metric summary values\n  @tracked ttfrP90Overall = 0;\n  @tracked ttfrP50Overall = 0;\n  @tracked ttfrAvgOverall = 0;\n  \n  // Goals tracking - 30 day\n  @tracked goalSev1TTFR = 0; // P90 TTFR for Sev1\n  @tracked goalOtherBugsTTFR = 0; // P90 TTFR for Sev2+3\n  @tracked goalForumResponse = 0; // Staff response % in all threads\n  @tracked goalSev1Coverage = 0; // Coverage rate for Sev1\n  \n  // Goals tracking - 7 day\n  @tracked goal7dSev1TTFR = 0;\n  @tracked goal7dOtherBugsTTFR = 0;\n  @tracked goal7dForumResponse = 0;\n  @tracked goal7dSev1Coverage = 0;\n  \n  @tracked slaData = {\n    sev1: { current: 95, target: 90 },\n    sev2: { current: 88, target: 85 },\n    sev3: { current: 78, target: 75 }\n  };\n  @tracked ttfrBySeverityData = {\n    sev1: { current: 0 },\n    sev2: { current: 0 },\n    sev3: { current: 0 }\n  };\n  @tracked coverageData = {\n    overall: 87,\n    sev1: 100,\n    sev2: 92,\n    sev3: 85,\n    bugs: 89,\n    features: 82,\n    other: 88\n  };\n  @tracked communityVsStaffData = {\n    communityPercent: 0,\n    staffPercent: 0\n  };\n  @tracked sentimentData = {\n    overall: 7.8,\n    excludingBugFeature: 8.2,\n    byCategory: [\n      { name: \"General\", score: 8.5 },\n      { name: \"Support\", score: 7.2 },\n      { name: \"Bugs\", score: 6.8 },\n      { name: \"Features\", score: 7.5 }\n    ]\n  };\n\n  // Data Explorer query definitions\n  queries = {\n    p90_ttfr: {\n      name: \"Forum Success - TTFR Metrics\",\n      sql: `-- [params]\n-- text :start_date\n-- text :end_date\n-- text :category_filter = all\n\nWITH first_responses AS (\n  SELECT \n    t.id as topic_id,\n    t.created_at as topic_created,\n    MIN(p.created_at) as first_response,\n    EXTRACT(EPOCH FROM (MIN(p.created_at) - t.created_at))/3600 as hours_to_response\n  FROM topics t\n  JOIN posts p ON p.topic_id = t.id\n  WHERE (:category_filter = 'all' OR t.category_id::text = :category_filter)\n    AND p.post_number > 1\n    AND p.user_id IN (\n      SELECT id FROM users WHERE admin = true OR moderator = true\n      UNION\n      SELECT user_id FROM group_users gu \n      JOIN groups g ON gu.group_id = g.id \n      WHERE g.name = 'CursorStaff'\n    )\n    AND t.created_at >= COALESCE(:start_date::timestamp, CURRENT_TIMESTAMP - INTERVAL '30 days')\n    AND t.created_at < COALESCE(:end_date::timestamp, CURRENT_TIMESTAMP)\n  GROUP BY t.id, t.created_at\n)\nSELECT \n  DATE_TRUNC('day', topic_created) as date,\n  PERCENTILE_CONT(0.9) WITHIN GROUP (ORDER BY hours_to_response) as p90_hours,\n  PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY hours_to_response) as p50_hours,\n  AVG(hours_to_response) as avg_hours\nFROM first_responses\nGROUP BY date\nORDER BY date`\n    },\n    sla_compliance: {\n      name: \"Forum Success - SLA Compliance\",\n      sql: `-- [params]\n-- text :start_date\n-- text :end_date\n\nWITH topic_first_posts AS (\n  SELECT \n    t.id as topic_id,\n    t.created_at,\n    p.raw as first_post_content\n  FROM topics t\n  JOIN posts p ON p.topic_id = t.id AND p.post_number = 1\n  WHERE t.category_id = 6\n    AND t.created_at >= COALESCE(:start_date::timestamp, CURRENT_TIMESTAMP - INTERVAL '30 days')\n    AND t.created_at < COALESCE(:end_date::timestamp, CURRENT_TIMESTAMP)\n),\ntopic_severities AS (\n  SELECT \n    topic_id,\n    created_at,\n    CASE \n      WHEN first_post_content ~* 'Yes - Cursor is unusable' THEN 1\n      WHEN first_post_content ~* 'Sometimes - I can sometimes use Cursor' THEN 2\n      WHEN first_post_content ~* 'No - Cursor works, but with this issue' THEN 3\n      ELSE NULL\n    END as severity,\n    CASE\n      WHEN first_post_content ~* 'Yes - Cursor is unusable' THEN 12\n      WHEN first_post_content ~* 'Sometimes - I can sometimes use Cursor' THEN 48\n      WHEN first_post_content ~* 'No - Cursor works, but with this issue' THEN 48\n      ELSE NULL\n    END as sla_hours\n  FROM topic_first_posts\n  WHERE first_post_content ~* 'Yes - Cursor is unusable|Sometimes - I can sometimes use Cursor|No - Cursor works, but with this issue'\n),\nfirst_responses AS (\n  SELECT \n    ts.topic_id,\n    ts.created_at,\n    ts.severity,\n    ts.sla_hours,\n    MIN(p.created_at) as first_response,\n    EXTRACT(EPOCH FROM (MIN(p.created_at) - ts.created_at))/3600 as hours_to_response\n  FROM topic_severities ts\n  JOIN posts p ON p.topic_id = ts.topic_id\n  WHERE p.post_number > 1\n    AND p.user_id IN (\n      SELECT id FROM users WHERE admin = true OR moderator = true\n      UNION\n      SELECT user_id FROM group_users gu \n      JOIN groups g ON gu.group_id = g.id \n      WHERE g.name = 'CursorStaff'\n    )\n  GROUP BY ts.topic_id, ts.created_at, ts.severity, ts.sla_hours\n)\nSELECT \n  DATE_TRUNC('day', created_at) as date,\n  severity,\n  COUNT(*) as total_topics,\n  COUNT(*) FILTER (WHERE hours_to_response <= sla_hours) as within_sla,\n  ROUND((100.0 * COUNT(*) FILTER (WHERE hours_to_response <= sla_hours) / COUNT(*))::numeric, 1) as sla_percentage\nFROM first_responses\nGROUP BY date, severity\nORDER BY date, severity`\n    },\n    coverage: {\n      name: \"Forum Success - Staff Response Coverage\",\n      sql: `-- [params]\n-- text :start_date\n-- text :end_date\n\nWITH topic_first_posts AS (\n  SELECT \n    t.id as topic_id,\n    t.created_at,\n    p.raw as first_post_content\n  FROM topics t\n  JOIN posts p ON p.topic_id = t.id AND p.post_number = 1\n  WHERE t.category_id = 6\n    AND t.created_at >= COALESCE(:start_date::timestamp, CURRENT_TIMESTAMP - INTERVAL '30 days')\n    AND t.created_at < COALESCE(:end_date::timestamp, CURRENT_TIMESTAMP)\n),\nrecent_topics AS (\n  SELECT \n    topic_id as id,\n    created_at,\n    CASE \n      WHEN first_post_content ~* 'Yes - Cursor is unusable' THEN 1\n      WHEN first_post_content ~* 'Sometimes - I can sometimes use Cursor' THEN 2\n      WHEN first_post_content ~* 'No - Cursor works, but with this issue' THEN 3\n      ELSE NULL\n    END as severity\n  FROM topic_first_posts\n),\ntopics_with_staff_response AS (\n  SELECT DISTINCT rt.id\n  FROM recent_topics rt\n  JOIN posts p ON p.topic_id = rt.id\n  WHERE p.post_number > 1\n    AND p.user_id IN (\n      SELECT id FROM users WHERE admin = true OR moderator = true\n      UNION\n      SELECT user_id FROM group_users gu \n      JOIN groups g ON gu.group_id = g.id \n      WHERE g.name = 'CursorStaff'\n    )\n    AND p.created_at < COALESCE(:end_date::timestamp, CURRENT_TIMESTAMP)\n)\n-- Severity coverage by date for charting\nSELECT \n  DATE_TRUNC('day', created_at) as date,\n  severity,\n  COUNT(*) as total,\n  COUNT(tsr.id) as with_response,\n  ROUND((100.0 * COUNT(tsr.id) / NULLIF(COUNT(*), 0))::numeric, 1) as coverage_percentage\nFROM recent_topics rt\nLEFT JOIN topics_with_staff_response tsr ON tsr.id = rt.id\nWHERE severity IS NOT NULL\nGROUP BY date, severity\n\nUNION ALL\n\n-- Severity breakdown (aggregate, not by date)\nSELECT \n  NULL as date,\n  severity,\n  COUNT(*) as total,\n  COUNT(tsr.id) as with_response,\n  ROUND((100.0 * COUNT(tsr.id) / NULLIF(COUNT(*), 0))::numeric, 1) as coverage_percentage\nFROM recent_topics rt\nLEFT JOIN topics_with_staff_response tsr ON tsr.id = rt.id\nWHERE severity IS NOT NULL\nGROUP BY severity\n\nORDER BY date NULLS LAST, severity`\n    },\n    community_vs_staff: {\n      name: \"Forum Success - Community vs Staff Replies\",\n      sql: `-- [params]\n-- text :start_date\n-- text :end_date\n\nWITH staff_user_ids AS (\n  SELECT id FROM users WHERE admin = true OR moderator = true\n  UNION\n  SELECT user_id FROM group_users gu \n  JOIN groups g ON gu.group_id = g.id \n  WHERE g.name = 'CursorStaff'\n),\ntopic_replies AS (\n  SELECT \n    t.id,\n    t.created_at,\n    COUNT(*) FILTER (WHERE p.post_number > 1 AND p.user_id IN (SELECT id FROM staff_user_ids)) as staff_replies,\n    COUNT(*) FILTER (WHERE p.post_number > 1 AND p.user_id NOT IN (SELECT id FROM staff_user_ids)) as community_replies\n  FROM topics t\n  JOIN posts p ON p.topic_id = t.id\n  WHERE t.created_at >= COALESCE(:start_date::timestamp, CURRENT_TIMESTAMP - INTERVAL '30 days')\n    AND t.created_at < COALESCE(:end_date::timestamp, CURRENT_TIMESTAMP)\n  GROUP BY t.id, t.created_at\n)\nSELECT \n  DATE_TRUNC('day', created_at) as date,\n  SUM(staff_replies) as staff_replies,\n  SUM(community_replies) as community_replies,\n  ROUND((100.0 * SUM(community_replies) / NULLIF(SUM(staff_replies + community_replies), 0))::numeric, 1) as community_percentage,\n  ROUND((100.0 * SUM(staff_replies) / NULLIF(SUM(staff_replies + community_replies), 0))::numeric, 1) as staff_percentage\nFROM topic_replies\nGROUP BY date\nORDER BY date`\n    },\n    sentiment: {\n      name: \"Forum Success - Sentiment Analysis\",\n      sql: `-- [params]\n-- text :start_date\n-- text :end_date\n\n-- Note: This is a placeholder - simple sentiment based on likes per post ratio\nWITH topic_scores AS (\n  SELECT \n    t.id,\n    t.created_at,\n    t.like_count,\n    t.posts_count,\n    -- Simple proxy: likes per post ratio\n    CASE \n      WHEN t.posts_count > 1 THEN LEAST(10, (t.like_count::float / t.posts_count) * 2)\n      ELSE 5.0\n    END as sentiment_score\n  FROM topics t\n  WHERE t.category_id = 6\n    AND t.created_at >= COALESCE(:start_date::timestamp, CURRENT_TIMESTAMP - INTERVAL '30 days')\n    AND t.created_at < COALESCE(:end_date::timestamp, CURRENT_TIMESTAMP)\n)\nSELECT \n  DATE_TRUNC('day', created_at) as date,\n  'overall' as category,\n  ROUND(AVG(sentiment_score)::numeric, 1) as avg_sentiment\nFROM topic_scores\nGROUP BY date\nORDER BY date`\n    },\n    bug_queue: {\n      name: \"Forum Success - Bug Response Queue\",\n      sql: `-- [params]\n-- text :start_date\n-- text :end_date\n\nWITH topic_first_posts AS (\n  SELECT \n    t.id as topic_id,\n    t.title,\n    t.created_at,\n    t.slug,\n    p.raw as first_post_content\n  FROM topics t\n  JOIN posts p ON p.topic_id = t.id AND p.post_number = 1\n  WHERE t.category_id = 6\n    AND t.created_at >= COALESCE(:start_date::timestamp, CURRENT_TIMESTAMP - INTERVAL '30 days')\n    AND t.created_at < COALESCE(:end_date::timestamp, CURRENT_TIMESTAMP)\n),\nrecent_topics AS (\n  SELECT \n    topic_id as id,\n    title,\n    created_at,\n    slug,\n    CASE \n      WHEN first_post_content ~* 'Yes - Cursor is unusable' THEN 1\n      WHEN first_post_content ~* 'Sometimes - I can sometimes use Cursor' THEN 2\n      WHEN first_post_content ~* 'No - Cursor works, but with this issue' THEN 3\n      ELSE NULL\n    END as severity\n  FROM topic_first_posts\n),\ntopics_with_staff_response AS (\n  SELECT DISTINCT rt.id\n  FROM recent_topics rt\n  JOIN posts p ON p.topic_id = rt.id\n  WHERE p.post_number > 1\n    AND p.user_id IN (\n      SELECT id FROM users WHERE admin = true OR moderator = true\n      UNION\n      SELECT user_id FROM group_users gu \n      JOIN groups g ON gu.group_id = g.id \n      WHERE g.name = 'CursorStaff'\n    )\n    AND p.created_at < COALESCE(:end_date::timestamp, CURRENT_TIMESTAMP)\n)\nSELECT \n  rt.id,\n  rt.title,\n  rt.created_at,\n  rt.slug,\n  rt.severity\nFROM recent_topics rt\nLEFT JOIN topics_with_staff_response tsr ON tsr.id = rt.id\nWHERE tsr.id IS NULL\n  AND rt.severity IS NOT NULL\nORDER BY rt.created_at ASC`\n    },\n    user_activity: {\n      name: \"Forum Success - User Activity\",\n      sql: `-- [params]\n-- text :start_date\n-- text :end_date\n\n-- Following Discourse's DAU/MAU calculation approach\nWITH dau AS (\n  SELECT \n    DATE_TRUNC('day', visited_at)::DATE AS date,\n    COUNT(DISTINCT user_id) AS dau\n  FROM user_visits\n  WHERE visited_at::DATE >= COALESCE(:start_date::timestamp, CURRENT_TIMESTAMP - INTERVAL '30 days')::DATE\n    AND visited_at <= COALESCE(:end_date::timestamp, CURRENT_TIMESTAMP)\n  GROUP BY DATE_TRUNC('day', visited_at)::DATE\n),\ndaily_posts AS (\n  SELECT \n    DATE_TRUNC('day', created_at)::date as post_date,\n    COUNT(DISTINCT user_id) as posting_users\n  FROM posts\n  WHERE created_at >= COALESCE(:start_date::timestamp, CURRENT_TIMESTAMP - INTERVAL '30 days')\n    AND created_at < COALESCE(:end_date::timestamp, CURRENT_TIMESTAMP)\n  GROUP BY DATE_TRUNC('day', created_at)::date\n),\ndaily_likes AS (\n  SELECT \n    DATE_TRUNC('day', created_at)::date as like_date,\n    COUNT(DISTINCT user_id) as liking_users\n  FROM post_actions\n  WHERE post_action_type_id = 2\n    AND created_at >= COALESCE(:start_date::timestamp, CURRENT_TIMESTAMP - INTERVAL '30 days')\n    AND created_at < COALESCE(:end_date::timestamp, CURRENT_TIMESTAMP)\n  GROUP BY DATE_TRUNC('day', created_at)::date\n)\nSELECT \n  dau.date,\n  dau.dau,\n  (SELECT COUNT(DISTINCT user_id)\n   FROM user_visits\n   WHERE visited_at::DATE BETWEEN dau.date - 29 AND dau.date\n  ) AS mau,\n  CASE \n    WHEN (SELECT COUNT(DISTINCT user_id) FROM user_visits WHERE visited_at::DATE BETWEEN dau.date - 29 AND dau.date) > 0\n    THEN ROUND(((dau.dau::numeric / (SELECT COUNT(DISTINCT user_id) FROM user_visits WHERE visited_at::DATE BETWEEN dau.date - 29 AND dau.date)::numeric) * 100)::numeric, 1)\n    ELSE 0\n  END as dau_mau_ratio,\n  COALESCE(dp.posting_users, 0) + COALESCE(dl.liking_users, 0) as engaged_users\nFROM dau\nLEFT JOIN daily_posts dp ON dp.post_date = dau.date\nLEFT JOIN daily_likes dl ON dl.like_date = dau.date\nORDER BY dau.date`\n    },\n    goals: {\n      name: \"Forum Success - Goals\",\n      sql: `-- Goals query for key metrics (trailing 30 days)\n\n-- Sev1 TTFR\nWITH sev1_topics AS (\n  SELECT \n    t.id as topic_id,\n    t.created_at\n  FROM topics t\n  JOIN posts p ON p.topic_id = t.id AND p.post_number = 1\n  WHERE t.category_id = 6\n    AND t.created_at >= CURRENT_TIMESTAMP - INTERVAL '30 days'\n    AND p.raw ~* 'Yes - Cursor is unusable'\n),\nsev1_responses AS (\n  SELECT \n    st.topic_id,\n    st.created_at,\n    MIN(p.created_at) as first_response,\n    EXTRACT(EPOCH FROM (MIN(p.created_at) - st.created_at))/3600 as hours_to_response\n  FROM sev1_topics st\n  JOIN posts p ON p.topic_id = st.topic_id\n  WHERE p.post_number > 1\n    AND p.user_id IN (\n      SELECT id FROM users WHERE admin = true OR moderator = true\n      UNION\n      SELECT user_id FROM group_users gu \n      JOIN groups g ON gu.group_id = g.id \n      WHERE g.name = 'CursorStaff'\n    )\n  GROUP BY st.topic_id, st.created_at\n),\nsev1_ttfr AS (\n  SELECT PERCENTILE_CONT(0.9) WITHIN GROUP (ORDER BY hours_to_response) as p90_hours\n  FROM sev1_responses\n),\n\n-- Sev2+3 TTFR\nother_sev_topics AS (\n  SELECT \n    t.id as topic_id,\n    t.created_at\n  FROM topics t\n  JOIN posts p ON p.topic_id = t.id AND p.post_number = 1\n  WHERE t.category_id = 6\n    AND t.created_at >= CURRENT_TIMESTAMP - INTERVAL '30 days'\n    AND (p.raw ~* 'Sometimes - I can sometimes use Cursor' OR p.raw ~* 'No - Cursor works, but with this issue')\n),\nother_sev_responses AS (\n  SELECT \n    ost.topic_id,\n    ost.created_at,\n    MIN(p.created_at) as first_response,\n    EXTRACT(EPOCH FROM (MIN(p.created_at) - ost.created_at))/3600 as hours_to_response\n  FROM other_sev_topics ost\n  JOIN posts p ON p.topic_id = ost.topic_id\n  WHERE p.post_number > 1\n    AND p.user_id IN (\n      SELECT id FROM users WHERE admin = true OR moderator = true\n      UNION\n      SELECT user_id FROM group_users gu \n      JOIN groups g ON gu.group_id = g.id \n      WHERE g.name = 'CursorStaff'\n    )\n  GROUP BY ost.topic_id, ost.created_at\n),\nother_sev_ttfr AS (\n  SELECT PERCENTILE_CONT(0.9) WITHIN GROUP (ORDER BY hours_to_response) as p90_hours\n  FROM other_sev_responses\n),\n\n-- Forum-wide staff response rate (topics with staff replies)\nforum_responses AS (\n  SELECT \n    COUNT(*) as total_topics,\n    COUNT(*) FILTER (\n      WHERE EXISTS (\n        SELECT 1 FROM posts p \n        WHERE p.topic_id = t.id \n          AND p.post_number > 1 \n          AND p.user_id IN (\n            SELECT id FROM users WHERE admin = true OR moderator = true\n            UNION\n            SELECT user_id FROM group_users gu \n            JOIN groups g ON gu.group_id = g.id \n            WHERE g.name = 'CursorStaff'\n          )\n      )\n    ) as topics_with_staff_reply\n  FROM topics t\n  WHERE t.created_at >= CURRENT_TIMESTAMP - INTERVAL '30 days'\n),\n\n-- Sev1 coverage\nsev1_coverage AS (\n  SELECT \n    COUNT(*) as total,\n    COUNT(*) FILTER (\n      WHERE EXISTS (\n        SELECT 1 FROM posts p \n        WHERE p.topic_id = t.id \n          AND p.post_number > 1 \n          AND p.user_id IN (\n            SELECT id FROM users WHERE admin = true OR moderator = true\n            UNION\n            SELECT user_id FROM group_users gu \n            JOIN groups g ON gu.group_id = g.id \n            WHERE g.name = 'CursorStaff'\n          )\n      )\n    ) as with_response\n  FROM topics t\n  JOIN posts fp ON fp.topic_id = t.id AND fp.post_number = 1\n  WHERE t.category_id = 6\n    AND t.created_at >= CURRENT_TIMESTAMP - INTERVAL '30 days'\n    AND fp.raw ~* 'Yes - Cursor is unusable'\n)\n\nSELECT \n  (SELECT p90_hours FROM sev1_ttfr) as sev1_p90_ttfr,\n  (SELECT p90_hours FROM other_sev_ttfr) as other_sev_p90_ttfr,\n  (SELECT ROUND((topics_with_staff_reply::numeric / NULLIF(total_topics, 0)::numeric) * 100, 1) FROM forum_responses) as staff_response_pct,\n  (SELECT ROUND((with_response::numeric / NULLIF(total, 0)::numeric) * 100, 1) FROM sev1_coverage) as sev1_coverage_pct`\n    },\n    goals_7d: {\n      name: \"Forum Success - Goals 7d\",\n      sql: `-- Goals query for key metrics (trailing 7 days with 48 hour grace period)\n\n-- Sev1 TTFR\nWITH sev1_topics AS (\n  SELECT \n    t.id as topic_id,\n    t.created_at\n  FROM topics t\n  JOIN posts p ON p.topic_id = t.id AND p.post_number = 1\n  WHERE t.category_id = 6\n    AND t.created_at >= CURRENT_TIMESTAMP - INTERVAL '9 days'\n    AND t.created_at < CURRENT_TIMESTAMP - INTERVAL '2 days'\n    AND p.raw ~* 'Yes - Cursor is unusable'\n),\nsev1_responses AS (\n  SELECT \n    st.topic_id,\n    st.created_at,\n    MIN(p.created_at) as first_response,\n    EXTRACT(EPOCH FROM (MIN(p.created_at) - st.created_at))/3600 as hours_to_response\n  FROM sev1_topics st\n  JOIN posts p ON p.topic_id = st.topic_id\n  WHERE p.post_number > 1\n    AND p.user_id IN (\n      SELECT id FROM users WHERE admin = true OR moderator = true\n      UNION\n      SELECT user_id FROM group_users gu \n      JOIN groups g ON gu.group_id = g.id \n      WHERE g.name = 'CursorStaff'\n    )\n  GROUP BY st.topic_id, st.created_at\n),\nsev1_ttfr AS (\n  SELECT PERCENTILE_CONT(0.9) WITHIN GROUP (ORDER BY hours_to_response) as p90_hours\n  FROM sev1_responses\n),\n\n-- Sev2+3 TTFR\nother_sev_topics AS (\n  SELECT \n    t.id as topic_id,\n    t.created_at\n  FROM topics t\n  JOIN posts p ON p.topic_id = t.id AND p.post_number = 1\n  WHERE t.category_id = 6\n    AND t.created_at >= CURRENT_TIMESTAMP - INTERVAL '9 days'\n    AND t.created_at < CURRENT_TIMESTAMP - INTERVAL '2 days'\n    AND (p.raw ~* 'Sometimes - I can sometimes use Cursor' OR p.raw ~* 'No - Cursor works, but with this issue')\n),\nother_sev_responses AS (\n  SELECT \n    ost.topic_id,\n    ost.created_at,\n    MIN(p.created_at) as first_response,\n    EXTRACT(EPOCH FROM (MIN(p.created_at) - ost.created_at))/3600 as hours_to_response\n  FROM other_sev_topics ost\n  JOIN posts p ON p.topic_id = ost.topic_id\n  WHERE p.post_number > 1\n    AND p.user_id IN (\n      SELECT id FROM users WHERE admin = true OR moderator = true\n      UNION\n      SELECT user_id FROM group_users gu \n      JOIN groups g ON gu.group_id = g.id \n      WHERE g.name = 'CursorStaff'\n    )\n  GROUP BY ost.topic_id, ost.created_at\n),\nother_sev_ttfr AS (\n  SELECT PERCENTILE_CONT(0.9) WITHIN GROUP (ORDER BY hours_to_response) as p90_hours\n  FROM other_sev_responses\n),\n\n-- Forum-wide staff response rate (topics with staff replies)\nforum_responses AS (\n  SELECT \n    COUNT(*) as total_topics,\n    COUNT(*) FILTER (\n      WHERE EXISTS (\n        SELECT 1 FROM posts p \n        WHERE p.topic_id = t.id \n          AND p.post_number > 1 \n          AND p.user_id IN (\n            SELECT id FROM users WHERE admin = true OR moderator = true\n            UNION\n            SELECT user_id FROM group_users gu \n            JOIN groups g ON gu.group_id = g.id \n            WHERE g.name = 'CursorStaff'\n          )\n      )\n    ) as topics_with_staff_reply\n  FROM topics t\n  WHERE t.created_at >= CURRENT_TIMESTAMP - INTERVAL '9 days'\n    AND t.created_at < CURRENT_TIMESTAMP - INTERVAL '2 days'\n),\n\n-- Sev1 coverage\nsev1_coverage AS (\n  SELECT \n    COUNT(*) as total,\n    COUNT(*) FILTER (\n      WHERE EXISTS (\n        SELECT 1 FROM posts p \n        WHERE p.topic_id = t.id \n          AND p.post_number > 1 \n          AND p.user_id IN (\n            SELECT id FROM users WHERE admin = true OR moderator = true\n            UNION\n            SELECT user_id FROM group_users gu \n            JOIN groups g ON gu.group_id = g.id \n            WHERE g.name = 'CursorStaff'\n          )\n      )\n    ) as with_response\n  FROM topics t\n  JOIN posts fp ON fp.topic_id = t.id AND fp.post_number = 1\n  WHERE t.category_id = 6\n    AND t.created_at >= CURRENT_TIMESTAMP - INTERVAL '9 days'\n    AND t.created_at < CURRENT_TIMESTAMP - INTERVAL '2 days'\n    AND fp.raw ~* 'Yes - Cursor is unusable'\n)\n\nSELECT \n  (SELECT p90_hours FROM sev1_ttfr) as sev1_p90_ttfr,\n  (SELECT p90_hours FROM other_sev_ttfr) as other_sev_p90_ttfr,\n  (SELECT ROUND((topics_with_staff_reply::numeric / NULLIF(total_topics, 0)::numeric) * 100, 1) FROM forum_responses) as staff_response_pct,\n  (SELECT ROUND((with_response::numeric / NULLIF(total, 0)::numeric) * 100, 1) FROM sev1_coverage) as sev1_coverage_pct`\n    },\n    ttfr_by_severity: {\n      name: \"Forum Success - TTFR by Severity\",\n      sql: `-- [params]\n-- text :start_date\n-- text :end_date\n\nWITH topic_first_posts AS (\n  SELECT \n    t.id as topic_id,\n    t.created_at,\n    p.raw as first_post_content\n  FROM topics t\n  JOIN posts p ON p.topic_id = t.id AND p.post_number = 1\n  WHERE t.category_id = 6\n    AND t.created_at >= COALESCE(:start_date::timestamp, CURRENT_TIMESTAMP - INTERVAL '30 days')\n    AND t.created_at < COALESCE(:end_date::timestamp, CURRENT_TIMESTAMP)\n),\ntopic_severities AS (\n  SELECT \n    topic_id,\n    created_at,\n    CASE \n      WHEN first_post_content ~* 'Yes - Cursor is unusable' THEN 1\n      WHEN first_post_content ~* 'Sometimes - I can sometimes use Cursor' THEN 2\n      WHEN first_post_content ~* 'No - Cursor works, but with this issue' THEN 3\n      ELSE NULL\n    END as severity\n  FROM topic_first_posts\n  WHERE first_post_content ~* 'Yes - Cursor is unusable|Sometimes - I can sometimes use Cursor|No - Cursor works, but with this issue'\n),\nfirst_responses AS (\n  SELECT \n    ts.topic_id,\n    ts.created_at,\n    ts.severity,\n    MIN(p.created_at) as first_response,\n    EXTRACT(EPOCH FROM (MIN(p.created_at) - ts.created_at))/3600 as hours_to_response\n  FROM topic_severities ts\n  JOIN posts p ON p.topic_id = ts.topic_id\n  WHERE p.post_number > 1\n    AND p.user_id IN (\n      SELECT id FROM users WHERE admin = true OR moderator = true\n      UNION\n      SELECT user_id FROM group_users gu \n      JOIN groups g ON gu.group_id = g.id \n      WHERE g.name = 'CursorStaff'\n    )\n  GROUP BY ts.topic_id, ts.created_at, ts.severity\n),\nseverity_ttfr AS (\n  SELECT \n    severity,\n    PERCENTILE_CONT(0.9) WITHIN GROUP (ORDER BY hours_to_response) as p90_hours\n  FROM first_responses\n  GROUP BY severity\n)\nSELECT \n  severity,\n  ROUND(p90_hours::numeric, 1) as p90_ttfr\nFROM severity_ttfr\nORDER BY severity`\n    }\n  };\n\n  constructor() {\n    super(...arguments);\n    this.checkAuthAndLoad();\n  }\n\n  @action\n  async checkAuthAndLoad() {\n    // Check if user is staff (moderator, admin, or CursorStaff group member)\n    const isCursorStaff = this.currentUser?.groups?.some(group => group.name === 'CursorStaff');\n    if (!this.currentUser || (!this.currentUser.staff && !isCursorStaff)) {\n      this.router.transitionTo(\"/\");\n      return;\n    }\n    this.loadData();\n  }\n\n  async ensureSingleQuery(key) {\n    try {\n      const queryDef = this.queries[key];\n      const existingQuery = await this.findQueryByName(queryDef.name);\n      \n      if (existingQuery) {\n        // Update existing query to ensure it has the latest SQL\n        console.log(`Updating query ${key} with new SQL`);\n        await this.updateQuery(existingQuery.id, queryDef);\n        this.queryIds[key] = existingQuery.id;\n      } else {\n        console.log(`Creating new query ${key}`);\n        const newQuery = await this.createQuery(queryDef);\n        this.queryIds[key] = newQuery.id;\n      }\n      return this.queryIds[key];\n    } catch (error) {\n      console.error(`Error ensuring query ${key}:`, error);\n      throw error;\n    }\n  }\n\n  async findQueryByName(name) {\n    try {\n      const response = await ajax(\"/admin/plugins/explorer/queries\");\n      return response.queries?.find(q => q.name === name);\n    } catch (error) {\n      console.error(\"Error finding query:\", error);\n      return null;\n    }\n  }\n\n  async createQuery(queryDef) {\n    try {\n      const response = await ajax(\"/admin/plugins/explorer/queries\", {\n        type: \"POST\",\n        data: {\n          query: {\n            name: queryDef.name,\n            sql: queryDef.sql,\n            description: \"Auto-generated query for Forum Success Stats\"\n          }\n        }\n      });\n      return response.query;\n    } catch (error) {\n      console.error(\"Error creating query:\", error);\n      throw error;\n    }\n  }\n\n  async updateQuery(queryId, queryDef) {\n    try {\n      const response = await ajax(`/admin/plugins/explorer/queries/${queryId}`, {\n        type: \"PUT\",\n        data: {\n          query: {\n            name: queryDef.name,\n            sql: queryDef.sql,\n            description: \"Auto-generated query for Forum Success Stats\"\n          }\n        }\n      });\n      return response.query;\n    } catch (error) {\n      console.error(\"Error updating query:\", error);\n      throw error;\n    }\n  }\n\n  async runQuery(queryId, params = {}) {\n    // Generate cache key from queryId and params\n    const cacheKey = `${queryId}_${JSON.stringify(params)}`;\n    \n    // Check if we have a cached result\n    if (this.queryCache[cacheKey]) {\n      console.log(`Using cached result for query ${queryId}`);\n      return this.queryCache[cacheKey];\n    }\n    \n    try {\n      const response = await ajax(`/admin/plugins/explorer/queries/${queryId}/run`, {\n        type: \"POST\",\n        data: {\n          params: JSON.stringify(params)\n        }\n      });\n      \n      // Cache the result\n      this.queryCache[cacheKey] = response;\n      console.log(`Cached result for query ${queryId}`);\n      \n      return response;\n    } catch (error) {\n      console.error(`Error running query ${queryId}:`, error);\n      throw error;\n    }\n  }\n\n  @action\n  async loadData() {\n    this.loading = true;\n    this.error = null;\n    \n    // Increment generation counter to track this load operation\n    this.currentQueryGeneration++;\n    const thisGeneration = this.currentQueryGeneration;\n    console.log(`Starting loadData generation ${thisGeneration}`);\n\n    try {\n      // Prepare date params - calculate defaults based on selected period\n      let startDate, endDate;\n      \n      if (this.startDate && this.endDate) {\n        startDate = this.startDate;\n        endDate = this.endDate;\n      } else {\n        // Use period to calculate date range\n        const now = moment();\n        endDate = now.format('YYYY-MM-DD HH:mm:ss');\n        \n        switch (this.selectedPeriod) {\n          case 'weekly':\n            startDate = moment().subtract(7, 'days').format('YYYY-MM-DD');\n            endDate = now.format('YYYY-MM-DD');\n            break;\n          case 'fortnight':\n            startDate = moment().subtract(14, 'days').format('YYYY-MM-DD');\n            endDate = now.format('YYYY-MM-DD');\n            break;\n          case 'month':\n            startDate = moment().subtract(30, 'days').format('YYYY-MM-DD');\n            endDate = now.format('YYYY-MM-DD');\n            break;\n          case '6months':\n            startDate = moment().subtract(6, 'months').format('YYYY-MM-DD');\n            endDate = now.format('YYYY-MM-DD');\n            break;\n          case '12months':\n            startDate = moment().subtract(12, 'months').format('YYYY-MM-DD');\n            endDate = now.format('YYYY-MM-DD');\n            break;\n          default:\n            startDate = moment().subtract(30, 'days').format('YYYY-MM-DD');\n            endDate = now.format('YYYY-MM-DD');\n        }\n      }\n\n      // Generate chart labels upfront based on date range\n      this.generateChartLabels(startDate, endDate);\n\n      const params = {\n        start_date: startDate,\n        end_date: endDate\n      };\n\n      // P90 params with category filter (always bug reports - category 6)\n      const p90Params = {\n        ...params,\n        category_filter: \"6\"\n      };\n\n      // Ensure and run queries sequentially so they appear one at a time\n      console.log('Starting queries sequentially at:', new Date().toISOString());\n      const startTime = Date.now();\n      \n      // Goals (always trailing 30 days)\n      try {\n        const goalsQueryId = await this.ensureSingleQuery('goals');\n        const goalsResult = await this.runQuery(goalsQueryId, {});\n        console.log(`Goals completed at ${Date.now() - startTime}ms`);\n        if (thisGeneration === this.currentQueryGeneration) {\n          this.processGoalsData(goalsResult);\n          next(() => this.loadingGoals = false);\n        } else {\n          console.log(`Skipping stale goals data from generation ${thisGeneration}, current is ${this.currentQueryGeneration}`);\n        }\n      } catch (err) {\n        console.error('Goals error:', err);\n        if (thisGeneration === this.currentQueryGeneration) {\n          next(() => this.loadingGoals = false);\n        }\n      }\n      \n      // Goals 7d (always trailing 7 days)\n      try {\n        const goals7dQueryId = await this.ensureSingleQuery('goals_7d');\n        const goals7dResult = await this.runQuery(goals7dQueryId, {});\n        console.log(`Goals 7d completed at ${Date.now() - startTime}ms`);\n        if (thisGeneration === this.currentQueryGeneration) {\n          this.processGoals7dData(goals7dResult);\n        } else {\n          console.log(`Skipping stale goals 7d data from generation ${thisGeneration}`);\n        }\n      } catch (err) {\n        console.error('Goals 7d error:', err);\n      }\n      \n      // P90 TTFR\n      try {\n        const p90QueryId = await this.ensureSingleQuery('p90_ttfr');\n        const p90Result = await this.runQuery(p90QueryId, p90Params);\n        console.log(`P90 completed at ${Date.now() - startTime}ms`);\n        if (thisGeneration === this.currentQueryGeneration) {\n          this.processP90Data(p90Result);\n          next(() => this.loadingP90 = false);\n        } else {\n          console.log(`Skipping stale P90 data from generation ${thisGeneration}`);\n        }\n      } catch (err) {\n        console.error('P90 error:', err);\n        if (thisGeneration === this.currentQueryGeneration) {\n          next(() => this.loadingP90 = false);\n        }\n      }\n      \n      // SLA Compliance\n      try {\n        const slaQueryId = await this.ensureSingleQuery('sla_compliance');\n        const slaResult = await this.runQuery(slaQueryId, params);\n        console.log(`SLA completed at ${Date.now() - startTime}ms`);\n        if (thisGeneration === this.currentQueryGeneration) {\n          this.processSLAData(slaResult);\n          next(() => this.loadingSLA = false);\n        } else {\n          console.log(`Skipping stale SLA data from generation ${thisGeneration}`);\n        }\n      } catch (err) {\n        console.error('SLA error:', err);\n        if (thisGeneration === this.currentQueryGeneration) {\n          next(() => this.loadingSLA = false);\n        }\n      }\n      \n      // TTFR by Severity\n      try {\n        const ttfrBySevQueryId = await this.ensureSingleQuery('ttfr_by_severity');\n        const ttfrBySevResult = await this.runQuery(ttfrBySevQueryId, params);\n        console.log(`TTFR by Severity completed at ${Date.now() - startTime}ms`);\n        if (thisGeneration === this.currentQueryGeneration) {\n          this.processTTFRBySeverityData(ttfrBySevResult);\n          next(() => this.loadingTTFRBySeverity = false);\n        } else {\n          console.log(`Skipping stale TTFR by Severity data from generation ${thisGeneration}`);\n        }\n      } catch (err) {\n        console.error('TTFR by Severity error:', err);\n        if (thisGeneration === this.currentQueryGeneration) {\n          next(() => this.loadingTTFRBySeverity = false);\n        }\n      }\n      \n      // Coverage\n      try {\n        const coverageQueryId = await this.ensureSingleQuery('coverage');\n        const coverageResult = await this.runQuery(coverageQueryId, params);\n        console.log(`Coverage completed at ${Date.now() - startTime}ms`);\n        if (thisGeneration === this.currentQueryGeneration) {\n          this.processCoverageData(coverageResult);\n          next(() => this.loadingCoverage = false);\n        } else {\n          console.log(`Skipping stale Coverage data from generation ${thisGeneration}`);\n        }\n      } catch (err) {\n        console.error('Coverage error:', err);\n        if (thisGeneration === this.currentQueryGeneration) {\n          next(() => this.loadingCoverage = false);\n        }\n      }\n      \n      // Community vs Staff\n      try {\n        const communityQueryId = await this.ensureSingleQuery('community_vs_staff');\n        const communityResult = await this.runQuery(communityQueryId, params);\n        console.log(`Community vs Staff completed at ${Date.now() - startTime}ms`);\n        if (thisGeneration === this.currentQueryGeneration) {\n          this.processCommunityVsStaffData(communityResult);\n          next(() => this.loadingCommunityVsStaff = false);\n        } else {\n          console.log(`Skipping stale Community vs Staff data from generation ${thisGeneration}`);\n        }\n      } catch (err) {\n        console.error('Community vs Staff error:', err);\n        if (thisGeneration === this.currentQueryGeneration) {\n          next(() => this.loadingCommunityVsStaff = false);\n        }\n      }\n      \n      // Sentiment\n      try {\n        const sentimentQueryId = await this.ensureSingleQuery('sentiment');\n        const sentimentResult = await this.runQuery(sentimentQueryId, params);\n        console.log(`Sentiment completed at ${Date.now() - startTime}ms`);\n        if (thisGeneration === this.currentQueryGeneration) {\n          this.processSentimentData(sentimentResult);\n          next(() => this.loadingSentiment = false);\n        } else {\n          console.log(`Skipping stale Sentiment data from generation ${thisGeneration}`);\n        }\n      } catch (err) {\n        console.error('Sentiment error:', err);\n        if (thisGeneration === this.currentQueryGeneration) {\n          next(() => this.loadingSentiment = false);\n        }\n      }\n      \n      // Bug Queue\n      try {\n        const queueQueryId = await this.ensureSingleQuery('bug_queue');\n        const queueResult = await this.runQuery(queueQueryId, params);\n        console.log(`Queue completed at ${Date.now() - startTime}ms`);\n        if (thisGeneration === this.currentQueryGeneration) {\n          this.processQueueData(queueResult);\n          next(() => this.loadingQueue = false);\n        } else {\n          console.log(`Skipping stale Queue data from generation ${thisGeneration}`);\n        }\n      } catch (err) {\n        console.error('Queue error:', err);\n        if (thisGeneration === this.currentQueryGeneration) {\n          next(() => this.loadingQueue = false);\n        }\n      }\n      \n      // User Activity\n      try {\n        const activityQueryId = await this.ensureSingleQuery('user_activity');\n        const activityResult = await this.runQuery(activityQueryId, params);\n        console.log(`Activity completed at ${Date.now() - startTime}ms`);\n        if (thisGeneration === this.currentQueryGeneration) {\n          this.processActivityData(activityResult);\n          next(() => this.loadingActivity = false);\n        } else {\n          console.log(`Skipping stale Activity data from generation ${thisGeneration}`);\n        }\n      } catch (err) {\n        console.error('Activity error:', err);\n        if (thisGeneration === this.currentQueryGeneration) {\n          next(() => this.loadingActivity = false);\n        }\n      }\n      \n      console.log(`All queries completed in ${Date.now() - startTime}ms`);\n    } catch (error) {\n      console.error(\"Error loading success stats:\", error);\n      this.error = \"Failed to load success stats. Check that Data Explorer queries are configured correctly.\";\n      popupAjaxError(error);\n    }\n  }\n\n  generateChartLabels(startDate, endDate) {\n    const start = moment(startDate);\n    const end = moment(endDate);\n    \n    const labels = [];\n    \n    // Always use daily intervals\n    let current = start.clone();\n    while (current.isSameOrBefore(end)) {\n      labels.push(current.format('MMM D'));\n      current.add(1, 'days');\n    }\n    \n    this.chartLabels = labels;\n  }\n\n  calculateTrailingPercentile(dataArray, trailingDays, percentile = 0.9) {\n    if (dataArray.length === 0) return [];\n    \n    const trailing = [];\n    for (let i = 0; i < dataArray.length; i++) {\n      const start = Math.max(0, i - (trailingDays - 1));\n      const window = dataArray.slice(start, i + 1).sort((a, b) => a - b);\n      const index = Math.floor(window.length * percentile);\n      trailing.push(Math.round(window[index] * 10) / 10);\n    }\n    return trailing;\n  }\n\n  applyP90ViewMode() {\n    // Apply view mode to P90 chart\n    if (this.p90ViewMode === '1day') {\n      // 1 day - use raw data as-is\n      this.p90ChartData = [...this.rawP90Data];\n    } else if (this.p90ViewMode === '7day') {\n      // 7 days - rolling 7-day P90 average\n      this.p90ChartData = this.calculateTrailingPercentile(this.rawP90Data, 7);\n    } else if (this.p90ViewMode === '30day') {\n      // 30 days - rolling 30-day P90 average\n      this.p90ChartData = this.calculateTrailingPercentile(this.rawP90Data, 30);\n    }\n  }\n\n  processP90Data(response) {\n    console.log(\"TTFR response:\", response);\n    if (!response || !response.rows || response.rows.length === 0) {\n      console.warn(\"TTFR: No data returned\");\n      throw new Error(\"TTFR query returned no data\");\n    }\n\n    console.log(\"TTFR: Using real data, rows:\", response.rows.length);\n    const rows = response.rows;\n    \n    // row[0] = date, row[1] = p90_hours, row[2] = p50_hours, row[3] = avg_hours\n    this.rawP90Data = rows.map(r => parseFloat(r[1]) || 0);\n    this.rawP50Data = rows.map(r => parseFloat(r[2]) || 0);\n    this.rawAvgData = rows.map(r => parseFloat(r[3]) || 0);\n    \n    // Calculate overall values for display in cards (median of all daily values)\n    const calcMedian = (arr) => {\n      if (!arr || arr.length === 0) return 0;\n      const sorted = [...arr].sort((a, b) => a - b);\n      const mid = Math.floor(sorted.length / 2);\n      return sorted.length % 2 === 0\n        ? (sorted[mid - 1] + sorted[mid]) / 2\n        : sorted[mid];\n    };\n    \n    const calcAverage = (arr) => {\n      if (!arr || arr.length === 0) return 0;\n      const sum = arr.reduce((sum, val) => sum + (parseFloat(val) || 0), 0);\n      return sum / arr.length;\n    };\n    \n    this.ttfrP90Overall = Math.round(calcMedian(this.rawP90Data) * 10) / 10;\n    this.ttfrP50Overall = Math.round(calcMedian(this.rawP50Data) * 10) / 10;\n    this.ttfrAvgOverall = Math.round(calcAverage(this.rawAvgData) * 10) / 10;\n    \n    // Set chart data based on selected metric type\n    this.updateTTFRChartData();\n  }\n  \n  updateTTFRChartData() {\n    // Update chart data based on selected metric\n    if (this.ttfrMetricType === \"p90\") {\n      this.p90ChartData = [...this.rawP90Data];\n    } else if (this.ttfrMetricType === \"p50\") {\n      this.p90ChartData = [...this.rawP50Data];\n    } else if (this.ttfrMetricType === \"average\") {\n      this.p90ChartData = [...this.rawAvgData];\n    }\n  }\n\n  aggregateDailyToWeekly(dailyData, startDate) {\n    if (dailyData.length === 0) return [];\n    \n    const weekly = [];\n    const numWeeks = this.chartLabels.length;\n    const start = moment(startDate);\n    \n    for (let i = 0; i < numWeeks; i++) {\n      const weekStart = start.clone().add(i, 'weeks');\n      const weekEnd = weekStart.clone().add(7, 'days');\n      \n      // Get all daily values in this week\n      const weekValues = dailyData\n        .filter(d => {\n          const date = moment(d.date);\n          return date.isSameOrAfter(weekStart) && date.isBefore(weekEnd);\n        })\n        .map(d => d.value);\n      \n      if (weekValues.length > 0) {\n        // Calculate p90 of this week's daily p90s\n        const sorted = weekValues.sort((a, b) => a - b);\n        const p90Index = Math.floor(sorted.length * 0.9);\n        weekly.push(sorted[p90Index] || sorted[sorted.length - 1]);\n      } else {\n        weekly.push(0);\n      }\n    }\n    \n    return weekly;\n  }\n\n  processSLAData(response) {\n    if (!response || !response.rows || response.rows.length === 0) {\n      console.warn(\"SLA Compliance: No data returned\");\n      throw new Error(\"SLA Compliance query returned no data\");\n    }\n\n    const rows = response.rows;\n    const dates = [...new Set(rows.map(r => r[0]))];\n\n    // Group by severity for chart\n    const sev1Data = [];\n    const sev2Data = [];\n    const sev3Data = [];\n    \n    // Track totals for overall calculation\n    let sev1TotalTopics = 0, sev1WithinSLA = 0;\n    let sev2TotalTopics = 0, sev2WithinSLA = 0;\n    let sev3TotalTopics = 0, sev3WithinSLA = 0;\n\n    dates.forEach(date => {\n      const sev1 = rows.find(r => r[0] === date && r[1] === 1);\n      const sev2 = rows.find(r => r[0] === date && r[1] === 2);\n      const sev3 = rows.find(r => r[0] === date && r[1] === 3);\n      \n      sev1Data.push(sev1?.[4] || 0);\n      sev2Data.push(sev2?.[4] || 0);\n      sev3Data.push(sev3?.[4] || 0);\n      \n      // Accumulate totals (columns: date, severity, total_topics, within_sla, sla_percentage)\n      if (sev1) {\n        sev1TotalTopics += sev1[2] || 0;\n        sev1WithinSLA += sev1[3] || 0;\n      }\n      if (sev2) {\n        sev2TotalTopics += sev2[2] || 0;\n        sev2WithinSLA += sev2[3] || 0;\n      }\n      if (sev3) {\n        sev3TotalTopics += sev3[2] || 0;\n        sev3WithinSLA += sev3[3] || 0;\n      }\n    });\n\n    // Use live data directly\n    this.sev1ChartData = sev1Data;\n    this.sev2ChartData = sev2Data;\n    this.sev3ChartData = sev3Data;\n\n    // Calculate overall compliance for the entire period\n    const sev1Overall = sev1TotalTopics > 0 \n      ? Math.round((sev1WithinSLA / sev1TotalTopics) * 100) \n      : 0;\n    const sev2Overall = sev2TotalTopics > 0 \n      ? Math.round((sev2WithinSLA / sev2TotalTopics) * 100) \n      : 0;\n    const sev3Overall = sev3TotalTopics > 0 \n      ? Math.round((sev3WithinSLA / sev3TotalTopics) * 100) \n      : 0;\n\n    this.slaData = {\n      sev1: { current: sev1Overall, target: 90 },\n      sev2: { current: sev2Overall, target: 85 },\n      sev3: { current: sev3Overall, target: 75 }\n    };\n  }\n\n  processCoverageData(response) {\n    if (!response || !response.rows || response.rows.length === 0) {\n      console.warn(\"Coverage: No data returned\");\n      throw new Error(\"Coverage query returned no data\");\n    }\n\n    const rows = response.rows;\n\n    // Rows with dates are for the chart (by severity)\n    const dateRows = rows.filter(r => r[0] !== null);\n    const dates = [...new Set(dateRows.map(r => r[0]))];\n\n    // Rows without dates are severity breakdowns (aggregates)\n    const sev1Agg = rows.find(r => r[0] === null && r[1] === 1);\n    const sev2Agg = rows.find(r => r[0] === null && r[1] === 2);\n    const sev3Agg = rows.find(r => r[0] === null && r[1] === 3);\n\n    // Create arrays aligned to dates\n    const sev1Data = [];\n    const sev2Data = [];\n    const sev3Data = [];\n\n    dates.forEach(date => {\n      const sev1Row = dateRows.find(r => r[0] === date && r[1] === 1);\n      const sev2Row = dateRows.find(r => r[0] === date && r[1] === 2);\n      const sev3Row = dateRows.find(r => r[0] === date && r[1] === 3);\n      \n      sev1Data.push(parseFloat(sev1Row?.[4]) || 0);\n      sev2Data.push(parseFloat(sev2Row?.[4]) || 0);\n      sev3Data.push(parseFloat(sev3Row?.[4]) || 0);\n    });\n\n    // Store chart data for each severity\n    this.coverageSev1ChartData = sev1Data;\n    this.coverageSev2ChartData = sev2Data;\n    this.coverageSev3ChartData = sev3Data;\n\n    // Calculate overall coverage from all severities across the period\n    let totalTopics = 0;\n    let totalWithResponse = 0;\n    \n    dateRows.forEach(r => {\n      totalTopics += parseInt(r[2]) || 0;  // total column\n      totalWithResponse += parseInt(r[3]) || 0;  // with_response column\n    });\n\n    const overallPercentage = totalTopics > 0 \n      ? Math.round((totalWithResponse / totalTopics) * 100) \n      : 0;\n\n    this.coverageData = {\n      overall: overallPercentage,\n      sev1: Math.round(parseFloat(sev1Agg?.[4]) || 0),\n      sev2: Math.round(parseFloat(sev2Agg?.[4]) || 0),\n      sev3: Math.round(parseFloat(sev3Agg?.[4]) || 0)\n    };\n  }\n\n  processCommunityVsStaffData(response) {\n    if (!response || !response.rows || response.rows.length === 0) {\n      console.warn(\"Community vs Staff: No data returned\");\n      throw new Error(\"Community vs Staff query returned no data\");\n    }\n\n    const rows = response.rows;\n    \n    // Store counts for chart and percentages for summary\n    this.communityVsStaffData_chart = rows.map(row => ({\n      community: parseInt(row[2]) || 0,    // community_replies count\n      staff: parseInt(row[1]) || 0         // staff_replies count\n    }));\n\n    // Calculate percentages for display from totals across the period\n    if (this.communityVsStaffData_chart.length === 0) {\n      throw new Error(\"Community vs Staff query returned no valid data\");\n    }\n\n    const totalCommunity = this.communityVsStaffData_chart.reduce((sum, d) => sum + (d.community || 0), 0);\n    const totalStaff = this.communityVsStaffData_chart.reduce((sum, d) => sum + (d.staff || 0), 0);\n    const total = totalCommunity + totalStaff;\n\n    this.communityVsStaffData = {\n      communityPercent: total > 0 ? Math.round((totalCommunity / total) * 100) : 0,\n      staffPercent: total > 0 ? Math.round((totalStaff / total) * 100) : 0\n    };\n  }\n\n  processSentimentData(response) {\n    if (!response || !response.rows || response.rows.length === 0) {\n      console.warn(\"Sentiment: No data returned\");\n      throw new Error(\"Sentiment query returned no data\");\n    }\n\n    const rows = response.rows;\n    \n    // Use live data directly\n    this.sentimentOverallData = rows.map(r => r[2] || 5.0);\n    \n    const latestScore = rows.length > 0 ? rows[rows.length - 1][2] : 7.8;\n    this.sentimentData = {\n      overall: latestScore,\n      excludingBugFeature: latestScore,  // Same value since all are bug reports\n      byCategory: []  // No category breakdown anymore\n    };\n  }\n\n  processQueueData(response) {\n    if (!response || !response.rows) {\n      console.warn(\"Queue: No data returned\");\n      this.queueBugs = [];\n      return;\n    }\n\n    const rows = response.rows;\n    \n    // Map response to bug objects\n    this.queueBugs = rows.map(row => ({\n      id: row[0],\n      title: row[1],\n      created_at: row[2],\n      slug: row[3],\n      severity: row[4]\n    }));\n  }\n\n  processActivityData(response) {\n    if (!response || !response.rows || response.rows.length === 0) {\n      console.warn(\"Activity: No data returned\");\n      this.dauChartData = [];\n      this.engagedUsersChartData = [];\n      this.dauMauRatioChartData = [];\n      return;\n    }\n\n    const rows = response.rows;\n    \n    // Extract activity data\n    // row[0] = date, row[1] = dau, row[2] = mau, row[3] = dau_mau_ratio, row[4] = engaged_users\n    this.dauChartData = rows.map(row => parseInt(row[1]) || 0);\n    this.engagedUsersChartData = rows.map(row => parseInt(row[4]) || 0);\n    this.dauMauRatioChartData = rows.map(row => parseFloat(row[3]) || 0);\n  }\n\n  processGoalsData(response) {\n    if (!response || !response.rows || response.rows.length === 0) {\n      console.warn(\"Goals: No data returned\");\n      return;\n    }\n\n    const row = response.rows[0];\n    // row[0] = sev1_p90_ttfr, row[1] = other_sev_p90_ttfr, row[2] = staff_response_pct, row[3] = sev1_coverage_pct\n    this.goalSev1TTFR = Math.round((parseFloat(row[0]) || 0) * 10) / 10;\n    this.goalOtherBugsTTFR = Math.round((parseFloat(row[1]) || 0) * 10) / 10;\n    this.goalForumResponse = Math.round((parseFloat(row[2]) || 0) * 10) / 10;\n    this.goalSev1Coverage = Math.round((parseFloat(row[3]) || 0) * 10) / 10;\n  }\n\n  processGoals7dData(response) {\n    if (!response || !response.rows || response.rows.length === 0) {\n      console.warn(\"Goals 7d: No data returned\");\n      return;\n    }\n\n    const row = response.rows[0];\n    // row[0] = sev1_p90_ttfr, row[1] = other_sev_p90_ttfr, row[2] = staff_response_pct, row[3] = sev1_coverage_pct\n    this.goal7dSev1TTFR = Math.round((parseFloat(row[0]) || 0) * 10) / 10;\n    this.goal7dOtherBugsTTFR = Math.round((parseFloat(row[1]) || 0) * 10) / 10;\n    this.goal7dForumResponse = Math.round((parseFloat(row[2]) || 0) * 10) / 10;\n    this.goal7dSev1Coverage = Math.round((parseFloat(row[3]) || 0) * 10) / 10;\n  }\n\n  processTTFRBySeverityData(response) {\n    if (!response || !response.rows || response.rows.length === 0) {\n      console.warn(\"TTFR by Severity: No data returned\");\n      return;\n    }\n\n    // Reset data\n    this.ttfrBySeverityData = {\n      sev1: { current: 0 },\n      sev2: { current: 0 },\n      sev3: { current: 0 }\n    };\n\n    // Process rows: row[0] = severity, row[1] = p90_ttfr\n    response.rows.forEach(row => {\n      const severity = parseInt(row[0]);\n      const p90 = parseFloat(row[1]) || 0;\n      \n      if (severity === 1) {\n        this.ttfrBySeverityData.sev1.current = Math.round(p90 * 10) / 10;\n      } else if (severity === 2) {\n        this.ttfrBySeverityData.sev2.current = Math.round(p90 * 10) / 10;\n      } else if (severity === 3) {\n        this.ttfrBySeverityData.sev3.current = Math.round(p90 * 10) / 10;\n      }\n    });\n  }\n\n  createChartConfig(data, label, color = \"#3498db\", showYAxis = true) {\n    return {\n      type: \"line\",\n      data: {\n        labels: this.chartLabels,\n        datasets: [{\n          label,\n          data: data.map(d => Math.round(parseFloat(d) * 10) / 10),\n          backgroundColor: \"transparent\",\n          borderColor: color,\n          pointRadius: 3,\n          borderWidth: 2,\n          pointBackgroundColor: color,\n          pointBorderColor: color,\n          tension: 0.4\n        }]\n      },\n      options: {\n        plugins: {\n          legend: {\n            display: false,\n          },\n        },\n        responsive: true,\n        maintainAspectRatio: false,\n        aspectRatio: 1,\n        scales: {\n          y: {\n            display: showYAxis,\n            beginAtZero: false,\n            grid: {\n              color: \"rgba(0, 0, 0, 0.05)\"\n            }\n          },\n          x: {\n            display: true,\n            type: \"category\",\n            grid: {\n              display: false\n            },\n            ticks: {\n              maxRotation: 45,\n              minRotation: 45\n            }\n          }\n        }\n      }\n    };\n  }\n\n  createMultiLineChartConfig(data, datasets, showYAxis = true) {\n    return {\n      type: \"line\",\n      data: {\n        labels: this.chartLabels,\n        datasets: datasets.map(ds => ({\n          label: ds.label,\n          data: data.map(d => Math.round(parseFloat(d[ds.key]) * 10) / 10),\n          backgroundColor: \"transparent\",\n          borderColor: ds.color,\n          pointRadius: 3,\n          borderWidth: 2,\n          pointBackgroundColor: ds.color,\n          pointBorderColor: ds.color,\n          tension: 0.4,\n          ...(ds.borderDash && { borderDash: ds.borderDash })\n        }))\n      },\n      options: {\n        plugins: {\n          legend: {\n            display: true,\n            position: \"top\"\n          },\n        },\n        responsive: true,\n        maintainAspectRatio: false,\n        aspectRatio: 1,\n        scales: {\n          y: {\n            display: showYAxis,\n            beginAtZero: false,\n            grid: {\n              color: \"rgba(0, 0, 0, 0.05)\"\n            }\n          },\n          x: {\n            display: true,\n            type: \"category\",\n            grid: {\n              display: false\n            },\n            ticks: {\n              maxRotation: 45,\n              minRotation: 45\n            }\n          }\n        }\n      }\n    };\n  }\n\n  get p90ChartConfig() {\n    if (!this.p90ChartData) return null;\n    \n    let label = \"TTFR (hours)\";\n    if (this.ttfrMetricType === \"p90\") {\n      label = \"P90 TTFR (hours)\";\n    } else if (this.ttfrMetricType === \"p50\") {\n      label = \"Median (P50) TTFR (hours)\";\n    } else if (this.ttfrMetricType === \"average\") {\n      label = \"Average TTFR (hours)\";\n    }\n    \n    return this.createChartConfig(this.p90ChartData, label, \"#3498db\");\n  }\n\n  get slaChartConfig() {\n    if (!this.sev1ChartData) return null;\n    const allData = this.sev1ChartData.map((d, i) => ({\n      sev1: this.sev1ChartData[i],\n      sev2: this.sev2ChartData[i],\n      sev3: this.sev3ChartData[i]\n    }));\n\n    return this.createMultiLineChartConfig(allData, [\n      { label: \"Sev 1\", key: \"sev1\", color: \"#27ae60\" },\n      { label: \"Sev 2\", key: \"sev2\", color: \"#3498db\" },\n      { label: \"Sev 3+\", key: \"sev3\", color: \"#9b59b6\" }\n    ]);\n  }\n\n  get coverageChartConfig() {\n    if (!this.coverageSev1ChartData) return null;\n    \n    const dataLength = this.coverageSev1ChartData.length;\n    const splitPoint = Math.max(0, dataLength - 2); // Last 2 points should be dashed\n    \n    // Create datasets with segment styling\n    const datasets = [\n      {\n        label: \"Sev 1\",\n        data: this.coverageSev1ChartData.map(d => Math.round(parseFloat(d) * 10) / 10),\n        backgroundColor: \"transparent\",\n        borderColor: \"#27ae60\",\n        pointRadius: 3,\n        borderWidth: 2,\n        pointBackgroundColor: \"#27ae60\",\n        pointBorderColor: \"#27ae60\",\n        tension: 0.4,\n        segment: {\n          borderDash: (ctx) => {\n            return ctx.p0DataIndex >= splitPoint - 1 ? [5, 5] : [];\n          }\n        }\n      },\n      {\n        label: \"Sev 2\",\n        data: this.coverageSev2ChartData.map(d => Math.round(parseFloat(d) * 10) / 10),\n        backgroundColor: \"transparent\",\n        borderColor: \"#3498db\",\n        pointRadius: 3,\n        borderWidth: 2,\n        pointBackgroundColor: \"#3498db\",\n        pointBorderColor: \"#3498db\",\n        tension: 0.4,\n        segment: {\n          borderDash: (ctx) => {\n            return ctx.p0DataIndex >= splitPoint - 1 ? [5, 5] : [];\n          }\n        }\n      },\n      {\n        label: \"Sev 3+\",\n        data: this.coverageSev3ChartData.map(d => Math.round(parseFloat(d) * 10) / 10),\n        backgroundColor: \"transparent\",\n        borderColor: \"#9b59b6\",\n        pointRadius: 3,\n        borderWidth: 2,\n        pointBackgroundColor: \"#9b59b6\",\n        pointBorderColor: \"#9b59b6\",\n        tension: 0.4,\n        segment: {\n          borderDash: (ctx) => {\n            return ctx.p0DataIndex >= splitPoint - 1 ? [5, 5] : [];\n          }\n        }\n      }\n    ];\n\n    return {\n      type: \"line\",\n      data: {\n        labels: this.chartLabels,\n        datasets: datasets\n      },\n      options: {\n        plugins: {\n          legend: {\n            display: true,\n            position: \"top\"\n          },\n        },\n        responsive: true,\n        maintainAspectRatio: false,\n        aspectRatio: 1,\n        scales: {\n          y: {\n            display: true,\n            beginAtZero: false,\n            grid: {\n              color: \"rgba(0, 0, 0, 0.05)\"\n            }\n          },\n          x: {\n            display: true,\n            type: \"category\",\n            grid: {\n              display: false\n            },\n            ticks: {\n              maxRotation: 45,\n              minRotation: 45\n            }\n          }\n        }\n      }\n    };\n  }\n\n  get communityVsStaffChartConfig() {\n    if (!this.communityVsStaffData_chart) return null;\n    return this.createMultiLineChartConfig(this.communityVsStaffData_chart, [\n      { label: \"Community Replies\", key: \"community\", color: \"#3498db\" },\n      { label: \"Staff Replies\", key: \"staff\", color: \"#9b59b6\" }\n    ]);\n  }\n\n  get sentimentChartConfig() {\n    if (!this.sentimentOverallData) return null;\n    return this.createChartConfig(this.sentimentOverallData, \"Sentiment Score\", \"#e67e22\");\n  }\n\n  get dauMauChartConfig() {\n    if (!this.dauMauRatioChartData || this.dauMauRatioChartData.length === 0) return null;\n    return this.createChartConfig(this.dauMauRatioChartData, \"DAU/MAU Ratio (%)\", \"#27ae60\");\n  }\n\n  get dauEngagementChartConfig() {\n    if (!this.dauChartData || this.dauChartData.length === 0) return null;\n    \n    const allData = this.dauChartData.map((d, i) => ({\n      dau: this.dauChartData[i],\n      engaged: this.engagedUsersChartData[i]\n    }));\n\n    return this.createMultiLineChartConfig(allData, [\n      { label: \"Daily Active Users\", key: \"dau\", color: \"#3498db\" },\n      { label: \"Daily Engaged Users\", key: \"engaged\", color: \"#e67e22\" }\n    ]);\n  }\n\n  @action\n  handlePeriodChange(event) {\n    const period = event.target.value;\n    this.changePeriod(period);\n  }\n\n  @action\n  changePeriod(period) {\n    this.selectedPeriod = period;\n    this.showCustomDates = false;\n    this.startDate = null;\n    this.endDate = null;\n    // Clear query cache when changing period\n    this.queryCache = {};\n    // Reset loading states to show skeletons\n    this.loadingGoals = true;\n    this.loadingP90 = true;\n    this.loadingSLA = true;\n    this.loadingTTFRBySeverity = true;\n    this.loadingCoverage = true;\n    this.loadingCommunityVsStaff = true;\n    this.loadingSentiment = true;\n    this.loadingQueue = true;\n    this.loadingActivity = true;\n    this.loadData();\n  }\n\n  @action\n  selectTTFRMetric(metricType) {\n    this.ttfrMetricType = metricType;\n    this.updateTTFRChartData();\n  }\n\n  @action\n  selectQueueTab(tab) {\n    this.queueSelectedTab = tab;\n  }\n\n  @action\n  toggleQueueSort() {\n    this.queueSortOrder = this.queueSortOrder === \"oldest\" ? \"newest\" : \"oldest\";\n  }\n\n  get filteredQueueBugs() {\n    let filtered = this.queueBugs;\n    \n    // Filter by severity tab\n    if (this.queueSelectedTab !== \"all\") {\n      const sevMap = { \"sev1\": 1, \"sev2\": 2, \"sev3\": 3 };\n      filtered = filtered.filter(bug => bug.severity === sevMap[this.queueSelectedTab]);\n    }\n    \n    // Sort\n    const sorted = [...filtered].sort((a, b) => {\n      const dateA = new Date(a.created_at);\n      const dateB = new Date(b.created_at);\n      return this.queueSortOrder === \"oldest\" ? dateA - dateB : dateB - dateA;\n    });\n    \n    return sorted;\n  }\n\n  @action\n  toggleCustomDates() {\n    this.showCustomDates = !this.showCustomDates;\n    if (!this.showCustomDates) {\n      this.startDate = null;\n      this.endDate = null;\n      // Clear query cache when toggling custom dates\n      this.queryCache = {};\n      // Reset loading states to show skeletons\n      this.loadingGoals = true;\n      this.loadingP90 = true;\n      this.loadingSLA = true;\n      this.loadingTTFRBySeverity = true;\n      this.loadingCoverage = true;\n      this.loadingCommunityVsStaff = true;\n      this.loadingSentiment = true;\n      this.loadingQueue = true;\n      this.loadingActivity = true;\n      this.loadData();\n    }\n  }\n\n  @action\n  updateStartDate(event) {\n    this.startDate = event.target.value;\n    if (this.startDate && this.endDate) {\n      // Clear query cache when dates change\n      this.queryCache = {};\n      // Reset loading states to show skeletons\n      this.loadingGoals = true;\n      this.loadingP90 = true;\n      this.loadingSLA = true;\n      this.loadingTTFRBySeverity = true;\n      this.loadingCoverage = true;\n      this.loadingCommunityVsStaff = true;\n      this.loadingSentiment = true;\n      this.loadingQueue = true;\n      this.loadingActivity = true;\n      this.loadData();\n    }\n  }\n\n  @action\n  updateEndDate(event) {\n    this.endDate = event.target.value;\n    if (this.startDate && this.endDate) {\n      // Clear query cache when dates change\n      this.queryCache = {};\n      // Reset loading states to show skeletons\n      this.loadingGoals = true;\n      this.loadingP90 = true;\n      this.loadingSLA = true;\n      this.loadingTTFRBySeverity = true;\n      this.loadingCoverage = true;\n      this.loadingCommunityVsStaff = true;\n      this.loadingSentiment = true;\n      this.loadingQueue = true;\n      this.loadingActivity = true;\n      this.loadData();\n    }\n  }\n\n  get dateRangeLabel() {\n    if (this.startDate && this.endDate) {\n      return `${moment(this.startDate).format(\"MMM D, YYYY\")} - ${moment(this.endDate).format(\"MMM D, YYYY\")}`;\n    }\n    return null;\n  }\n\n  // Goal status helpers\n  goalSev1Status(value) {\n    if (value < 12) return 'success';\n    if (value < 15) return 'warning';\n    return 'danger';\n  }\n\n  goalOtherBugsStatus(value) {\n    if (value < 48) return 'success';\n    if (value < 60) return 'warning';\n    return 'danger';\n  }\n\n  goalForumResponseStatus(value) {\n    if (value > 90) return 'success';\n    if (value > 85) return 'warning';\n    return 'danger';\n  }\n\n  goalSev1CoverageStatus(value) {\n    if (value >= 99) return 'success';\n    if (value > 85) return 'warning';\n    return 'danger';\n  }\n\n  get periodDescriptionText() {\n    if (this.showCustomDates && this.startDate && this.endDate) {\n      return \"the selected date range\";\n    }\n    \n    const periodMap = {\n      'weekly': 'the last 7 days',\n      'fortnight': 'the last 14 days',\n      'month': 'the last 30 days',\n      '6months': 'the last 6 months',\n      '12months': 'the last 12 months'\n    };\n    \n    return periodMap[this.selectedPeriod] || 'the selected period';\n  }\n\n  get periodShortText() {\n    if (this.showCustomDates) {\n      return \"custom\";\n    }\n    \n    const periodMap = {\n      'weekly': '7d',\n      'fortnight': '14d',\n      'month': '30d',\n      '6months': '6mo',\n      '12months': '12mo'\n    };\n    \n    return periodMap[this.selectedPeriod] || '30d';\n  }\n\n  get isStaffOrCursorStaff() {\n    const isCursorStaff = this.currentUser?.groups?.some(group => group.name === 'CursorStaff');\n    return this.currentUser && (this.currentUser.staff || isCursorStaff);\n  }\n\n  <template>\n    {{#if this.isStaffOrCursorStaff}}\n      <div class=\"forum-success-stats\">\n        <div class=\"stats-header\">\n          <div class=\"header-content\">\n            <div class=\"header-main\">\n              <h2>Forum Success Stats</h2>\n              <p class=\"header-description\">\n                Real-time metrics from <strong>bug reports</strong> in <strong>{{this.periodDescriptionText}}</strong>.<br />\n                Use the time period dropdown to filter all stats and charts.\n              </p>\n              {{#if this.dateRangeLabel}}\n                <span class=\"date-range-label\">{{this.dateRangeLabel}}</span>\n              {{/if}}\n            </div>\n            <div class=\"header-controls\">\n              {{#unless this.showCustomDates}}\n                <div class=\"period-selector\">\n                  <select \n                    class=\"period-dropdown\"\n                    {{on \"change\" this.handlePeriodChange}}\n                  >\n                    <option value=\"weekly\" selected={{eq this.selectedPeriod \"weekly\"}}>Last 7 days</option>\n                    <option value=\"fortnight\" selected={{eq this.selectedPeriod \"fortnight\"}}>Last 14 days</option>\n                    <option value=\"month\" selected={{eq this.selectedPeriod \"month\"}}>Last 30 days</option>\n                    <option value=\"6months\" selected={{eq this.selectedPeriod \"6months\"}}>Last 6 months</option>\n                    <option value=\"12months\" selected={{eq this.selectedPeriod \"12months\"}}>Last 12 months</option>\n                  </select>\n                </div>\n              {{/unless}}\n              \n              {{#if this.showCustomDates}}\n                <div class=\"date-picker-controls\">\n                  <label class=\"date-picker-label\">\n                    Start:\n                    <input \n                      type=\"date\" \n                      class=\"date-picker-input\"\n                      value={{this.startDate}}\n                      {{on \"change\" this.updateStartDate}}\n                    />\n                  </label>\n                  <label class=\"date-picker-label\">\n                    End:\n                    <input \n                      type=\"date\" \n                      class=\"date-picker-input\"\n                      value={{this.endDate}}\n                      {{on \"change\" this.updateEndDate}}\n                    />\n                  </label>\n                </div>\n              {{/if}}\n\n              <button \n                class=\"btn btn-default\" \n                {{on \"click\" this.toggleCustomDates}}\n              >\n                {{if this.showCustomDates \"Quick Select\" \"Custom Dates\"}}\n              </button>\n            </div>\n          </div>\n        </div>\n\n      {{#if this.error}}\n        <div class=\"stats-error\">\n          <p>{{this.error}}</p>\n        </div>\n      {{else}}\n        \n        {{! Goals Section }}\n        <div class=\"goals-section\">\n          <div class=\"goals-columns\">\n            <div class=\"goals-column\">\n              <h3>Key Goals (Trailing 30 Days)</h3>\n              {{#if this.loadingGoals}}\n                <div class=\"goals-grid skeleton-grid\">\n                  <div class=\"skeleton-card small\"></div>\n                  <div class=\"skeleton-card small\"></div>\n                  <div class=\"skeleton-card small\"></div>\n                  <div class=\"skeleton-card small\"></div>\n                </div>\n              {{else}}\n                <div class=\"goals-grid\">\n                  <div class=\"goal-card\">\n                    <span class=\"status-dot {{this.goalSev1Status this.goalSev1TTFR}}\"></span>\n                    <span class=\"goal-label\">Sev1 P90 TTFR</span>\n                    <span class=\"goal-value\">{{this.goalSev1TTFR}}h <span class=\"goal-target\">/ &lt;12h</span></span>\n                  </div>\n                  \n                  <div class=\"goal-card\">\n                    <span class=\"status-dot {{this.goalOtherBugsStatus this.goalOtherBugsTTFR}}\"></span>\n                    <span class=\"goal-label\">Other Bugs P90 TTFR</span>\n                    <span class=\"goal-value\">{{this.goalOtherBugsTTFR}}h <span class=\"goal-target\">/ &lt;48h</span></span>\n                  </div>\n                  \n                  <div class=\"goal-card\">\n                    <span class=\"status-dot {{this.goalForumResponseStatus this.goalForumResponse}}\"></span>\n                    <span class=\"goal-label\">Topics with Staff Reply</span>\n                    <span class=\"goal-value\">{{this.goalForumResponse}}% <span class=\"goal-target\">/ &gt;90%</span></span>\n                  </div>\n                  \n                  <div class=\"goal-card\">\n                    <span class=\"status-dot {{this.goalSev1CoverageStatus this.goalSev1Coverage}}\"></span>\n                    <span class=\"goal-label\">Sev1 Response Rate</span>\n                    <span class=\"goal-value\">{{this.goalSev1Coverage}}% <span class=\"goal-target\">/ 99%</span></span>\n                  </div>\n                </div>\n              {{/if}}\n            </div>\n\n            <div class=\"goals-column\">\n              <h3>Key Goals (Trailing 7 Days)</h3>\n              {{#if this.loadingGoals}}\n                <div class=\"goals-grid skeleton-grid\">\n                  <div class=\"skeleton-card small\"></div>\n                  <div class=\"skeleton-card small\"></div>\n                  <div class=\"skeleton-card small\"></div>\n                  <div class=\"skeleton-card small\"></div>\n                </div>\n              {{else}}\n                <div class=\"goals-grid\">\n                  <div class=\"goal-card\">\n                    <span class=\"status-dot {{this.goalSev1Status this.goal7dSev1TTFR}}\"></span>\n                    <span class=\"goal-label\">Sev1 P90 TTFR</span>\n                    <span class=\"goal-value\">{{this.goal7dSev1TTFR}}h <span class=\"goal-target\">/ &lt;12h</span></span>\n                  </div>\n                  \n                  <div class=\"goal-card\">\n                    <span class=\"status-dot {{this.goalOtherBugsStatus this.goal7dOtherBugsTTFR}}\"></span>\n                    <span class=\"goal-label\">Other Bugs P90 TTFR</span>\n                    <span class=\"goal-value\">{{this.goal7dOtherBugsTTFR}}h <span class=\"goal-target\">/ &lt;48h</span></span>\n                  </div>\n                  \n                  <div class=\"goal-card\">\n                    <span class=\"status-dot {{this.goalForumResponseStatus this.goal7dForumResponse}}\"></span>\n                    <span class=\"goal-label\">Topics with Staff Reply</span>\n                    <span class=\"goal-value\">{{this.goal7dForumResponse}}% <span class=\"goal-target\">/ &gt;90%</span></span>\n                  </div>\n                  \n                  <div class=\"goal-card\">\n                    <span class=\"status-dot {{this.goalSev1CoverageStatus this.goal7dSev1Coverage}}\"></span>\n                    <span class=\"goal-label\">Sev1 Response Rate</span>\n                    <span class=\"goal-value\">{{this.goal7dSev1Coverage}}% <span class=\"goal-target\">/ 99%</span></span>\n                  </div>\n                </div>\n              {{/if}}\n            </div>\n          </div>\n        </div>\n        \n        {{! 1. TTFR }}\n        <div class=\"stat-section\">\n          <div class=\"section-header\">\n            <h3>Time to First Response (TTFR)</h3>\n            <span class=\"section-subtitle\">Staff response times for bug reports in <strong>{{this.periodDescriptionText}}</strong></span>\n          </div>\n\n          {{#if this.loadingP90}}\n            {{! Loading skeleton }}\n            <div class=\"skeleton-grid three-col\">\n              <div class=\"skeleton-card\"></div>\n              <div class=\"skeleton-card\"></div>\n              <div class=\"skeleton-card\"></div>\n            </div>\n            <div class=\"skeleton-chart\"></div>\n          {{else}}\n            {{! Metric cards }}\n            <div class=\"metric-grid three-col\">\n              <div class=\"metric-card compact {{if (eq this.ttfrMetricType 'p90') 'selected'}}\" {{on \"click\" (fn this.selectTTFRMetric \"p90\")}}>\n                <div class=\"metric-label\">P90 TTFR</div>\n                <div class=\"metric-value small\">\n                  {{this.ttfrP90Overall}}\n                  <span class=\"metric-unit\">hours</span>\n                </div>\n              </div>\n              \n              <div class=\"metric-card compact {{if (eq this.ttfrMetricType 'p50') 'selected'}}\" {{on \"click\" (fn this.selectTTFRMetric \"p50\")}}>\n                <div class=\"metric-label\">Median (P50) TTFR</div>\n                <div class=\"metric-value small\">\n                  {{this.ttfrP50Overall}}\n                  <span class=\"metric-unit\">hours</span>\n                </div>\n              </div>\n              \n              <div class=\"metric-card compact {{if (eq this.ttfrMetricType 'average') 'selected'}}\" {{on \"click\" (fn this.selectTTFRMetric \"average\")}}>\n                <div class=\"metric-label\">Average TTFR</div>\n                <div class=\"metric-value small\">\n                  {{this.ttfrAvgOverall}}\n                  <span class=\"metric-unit\">hours</span>\n                </div>\n              </div>\n            </div>\n\n            {{! Chart }}\n            {{#if this.p90ChartConfig}}\n              <div class=\"chart-container\">\n                <div class=\"chart-wrapper\">\n                  <Chart @chartConfig={{this.p90ChartConfig}} />\n                </div>\n              </div>\n            {{/if}}\n          {{/if}}\n        </div>\n\n        {{! 2. SLA Compliance }}\n        <div class=\"stat-section\">\n          <div class=\"section-header\">\n            <h3>SLA Compliance</h3>\n            <span class=\"section-subtitle\">Percentage of posts in <strong>{{this.periodDescriptionText}}</strong> that were responded to within the target SLA by a staff member</span>\n          </div>\n          {{#if this.loadingSLA}}\n            <div class=\"skeleton-grid three-col\">\n              <div class=\"skeleton-card\"></div>\n              <div class=\"skeleton-card\"></div>\n              <div class=\"skeleton-card\"></div>\n            </div>\n            <div class=\"skeleton-chart\"></div>\n          {{else}}\n            <div class=\"metric-grid three-col\">\n              <div class=\"metric-card\">\n                <h4>Severity 1</h4>\n                <div class=\"severity-match-text\">\"Yes - Cursor is unusable\"</div>\n                <div class=\"metric-value\">\n                  {{this.slaData.sev1.current}}%\n                </div>\n                <div class=\"metric-target\">SLA: 12 hours</div>\n                <div class=\"progress-bar\">\n                  <div class=\"progress-fill sev1\" style=\"width: {{this.slaData.sev1.current}}%\"></div>\n                </div>\n              </div>\n              <div class=\"metric-card\">\n                <h4>Severity 2</h4>\n                <div class=\"severity-match-text\">\"Sometimes - I can sometimes use Cursor\"</div>\n                <div class=\"metric-value\">\n                  {{this.slaData.sev2.current}}%\n                </div>\n                <div class=\"metric-target\">SLA: 48 hours</div>\n                <div class=\"progress-bar\">\n                  <div class=\"progress-fill sev2\" style=\"width: {{this.slaData.sev2.current}}%\"></div>\n                </div>\n              </div>\n              <div class=\"metric-card\">\n                <h4>Severity 3+</h4>\n                <div class=\"severity-match-text\">\"No - Cursor works, but with this issue\"</div>\n                <div class=\"metric-value\">\n                  {{this.slaData.sev3.current}}%\n                </div>\n                <div class=\"metric-target\">SLA: 48 hours</div>\n                <div class=\"progress-bar\">\n                  <div class=\"progress-fill sev3\" style=\"width: {{this.slaData.sev3.current}}%\"></div>\n                </div>\n              </div>\n            </div>\n            {{#if this.slaChartConfig}}\n              <div class=\"chart-container\">\n                <div class=\"chart-wrapper\">\n                  <Chart @chartConfig={{this.slaChartConfig}} />\n                </div>\n              </div>\n            {{/if}}\n          {{/if}}\n        </div>\n\n        {{! 2b. TTFR by Severity }}\n        <div class=\"stat-section\">\n          <div class=\"section-header\">\n            <h3>P90 TTFR by Severity</h3>\n            <span class=\"section-subtitle\">90th percentile time to first response for bug reports in <strong>{{this.periodDescriptionText}}</strong>, grouped by severity level</span>\n          </div>\n          {{#if this.loadingTTFRBySeverity}}\n            <div class=\"skeleton-grid three-col\">\n              <div class=\"skeleton-card\"></div>\n              <div class=\"skeleton-card\"></div>\n              <div class=\"skeleton-card\"></div>\n            </div>\n          {{else}}\n            <div class=\"metric-grid three-col\">\n              <div class=\"metric-card\">\n                <h4>Severity 1</h4>\n                <div class=\"severity-match-text\">\"Yes - Cursor is unusable\"</div>\n                <div class=\"metric-value\">\n                  {{this.ttfrBySeverityData.sev1.current}}h\n                </div>\n                <div class=\"metric-target\">Target: &lt;12 hours</div>\n              </div>\n              <div class=\"metric-card\">\n                <h4>Severity 2</h4>\n                <div class=\"severity-match-text\">\"Sometimes - I can sometimes use Cursor\"</div>\n                <div class=\"metric-value\">\n                  {{this.ttfrBySeverityData.sev2.current}}h\n                </div>\n                <div class=\"metric-target\">Target: &lt;48 hours</div>\n              </div>\n              <div class=\"metric-card\">\n                <h4>Severity 3+</h4>\n                <div class=\"severity-match-text\">\"No - Cursor works, but with this issue\"</div>\n                <div class=\"metric-value\">\n                  {{this.ttfrBySeverityData.sev3.current}}h\n                </div>\n                <div class=\"metric-target\">Target: &lt;48 hours</div>\n              </div>\n            </div>\n          {{/if}}\n        </div>\n\n        {{! 3. Coverage Rate }}\n        <div class=\"stat-section\">\n          <div class=\"section-header\">\n            <h3>Bug Report Response Coverage</h3>\n            <span class=\"section-subtitle\">Percentage of bug reports in <strong>{{this.periodDescriptionText}}</strong> that received at least one staff reply</span>\n          </div>\n          {{#if this.loadingCoverage}}\n            <div class=\"skeleton-grid four-col\">\n              <div class=\"skeleton-card\"></div>\n              <div class=\"skeleton-card\"></div>\n              <div class=\"skeleton-card\"></div>\n              <div class=\"skeleton-card\"></div>\n            </div>\n            <div class=\"skeleton-chart\"></div>\n          {{else}}\n            <div class=\"metric-grid four-col\">\n              <div class=\"metric-card\">\n                <h4>Overall</h4>\n                <div class=\"metric-value\">\n                  {{this.coverageData.overall}}%\n                </div>\n                <div class=\"metric-target\">All severities combined</div>\n                <div class=\"progress-bar\">\n                  <div class=\"progress-fill overall\" style=\"width: {{this.coverageData.overall}}%\"></div>\n                </div>\n              </div>\n              <div class=\"metric-card\">\n                <h4>Severity 1</h4>\n                <div class=\"metric-value\">\n                  {{this.coverageData.sev1}}%\n                </div>\n                <div class=\"metric-target\">SLA: 12 hours</div>\n                <div class=\"progress-bar\">\n                  <div class=\"progress-fill sev1\" style=\"width: {{this.coverageData.sev1}}%\"></div>\n                </div>\n              </div>\n              <div class=\"metric-card\">\n                <h4>Severity 2</h4>\n                <div class=\"metric-value\">\n                  {{this.coverageData.sev2}}%\n                </div>\n                <div class=\"metric-target\">SLA: 48 hours</div>\n                <div class=\"progress-bar\">\n                  <div class=\"progress-fill sev2\" style=\"width: {{this.coverageData.sev2}}%\"></div>\n                </div>\n              </div>\n              <div class=\"metric-card\">\n                <h4>Severity 3+</h4>\n                <div class=\"metric-value\">\n                  {{this.coverageData.sev3}}%\n                </div>\n                <div class=\"metric-target\">SLA: 48 hours</div>\n                <div class=\"progress-bar\">\n                  <div class=\"progress-fill sev3\" style=\"width: {{this.coverageData.sev3}}%\"></div>\n                </div>\n              </div>\n            </div>\n            {{#if this.coverageChartConfig}}\n              <div class=\"chart-container\">\n                <div class=\"chart-wrapper\">\n                  <Chart @chartConfig={{this.coverageChartConfig}} />\n                </div>\n              </div>\n            {{/if}}\n          {{/if}}\n        </div>\n\n        {{! 4. Community vs Staff Answers }}\n        <div class=\"stat-section\">\n          <div class=\"section-header\">\n            <h3>Forum Engagement</h3>\n            <span class=\"section-subtitle\">Distribution of replies across the entire forum in <strong>{{this.periodDescriptionText}}</strong> by community members vs. staff members</span>\n          </div>\n          {{#if this.loadingCommunityVsStaff}}\n            <div class=\"skeleton-card\"></div>\n            <div class=\"skeleton-chart\"></div>\n          {{else}}\n            <div class=\"metric-card large\">\n              <div class=\"split-metric\">\n                <div class=\"split-item community\">\n                  <div class=\"split-value\">{{this.communityVsStaffData.communityPercent}}%</div>\n                  <div class=\"split-label\">Community</div>\n                </div>\n                <div class=\"split-divider\"></div>\n                <div class=\"split-item staff\">\n                  <div class=\"split-value\">{{this.communityVsStaffData.staffPercent}}%</div>\n                  <div class=\"split-label\">Staff</div>\n                </div>\n              </div>\n            </div>\n            {{#if this.communityVsStaffChartConfig}}\n              <div class=\"chart-container\">\n                <div class=\"chart-wrapper\">\n                  <Chart @chartConfig={{this.communityVsStaffChartConfig}} />\n                </div>\n              </div>\n            {{/if}}\n          {{/if}}\n        </div>\n\n        {{! 5. Sentiment / CSAT }}\n        <div class=\"stat-section\">\n          <div class=\"section-header\">\n            <h3>Bug Report Sentiment</h3>\n            <span class=\"section-subtitle\">Average satisfaction score (1-10) for bug reports in <strong>{{this.periodDescriptionText}}</strong>, calculated based on reply engagement and resolution indicators</span>\n          </div>\n          {{#if this.loadingSentiment}}\n            <div class=\"skeleton-card\"></div>\n            <div class=\"skeleton-chart\"></div>\n          {{else}}\n            <div class=\"metric-card large highlight\">\n              <div class=\"metric-value\">\n                {{this.sentimentData.overall}}\n                <span class=\"metric-unit\">/10</span>\n              </div>\n              <div class=\"metric-label\">Average Sentiment Score</div>\n            </div>\n\n\n            {{#if this.sentimentChartConfig}}\n              <div class=\"chart-container\">\n                <div class=\"chart-wrapper\">\n                  <Chart @chartConfig={{this.sentimentChartConfig}} />\n                </div>\n              </div>\n            {{/if}}\n          {{/if}}\n        </div>\n\n        {{! 6. User Activity }}\n        <div class=\"stat-section\">\n          <div class=\"section-header\">\n            <h3>User Activity</h3>\n            <span class=\"section-subtitle\">Community engagement metrics in <strong>{{this.periodDescriptionText}}</strong></span>\n          </div>\n          \n          {{#if this.loadingActivity}}\n            <div class=\"skeleton-grid two-col\">\n              <div class=\"skeleton-card\"></div>\n              <div class=\"skeleton-card\"></div>\n            </div>\n          {{else}}\n            <div class=\"metric-grid two-col\">\n              {{! DAU/MAU Ratio }}\n              <div class=\"metric-card compact\">\n                <h4>DAU/MAU Ratio</h4>\n                <p class=\"metric-description\">Number of members that logged in in on a given day, divided by number of members that logged in in the trailing month before that day. Indicates community 'stickiness'.</p>\n                {{#if this.dauMauChartConfig}}\n                  <div class=\"chart-container\">\n                    <div class=\"chart-wrapper\">\n                      <Chart @chartConfig={{this.dauMauChartConfig}} />\n                    </div>\n                  </div>\n                {{/if}}\n              </div>\n\n              {{! Daily Active Users vs Engaged Users }}\n              <div class=\"metric-card compact\">\n                <h4>Daily Active Users</h4>\n                <p class=\"metric-description\">DAU (users who logged in) vs. Daily Engaged Users (users who liked, created or replied to a post) within {{this.periodDescriptionText}}.</p>\n                {{#if this.dauEngagementChartConfig}}\n                  <div class=\"chart-container\">\n                    <div class=\"chart-wrapper\">\n                      <Chart @chartConfig={{this.dauEngagementChartConfig}} />\n                    </div>\n                  </div>\n                {{/if}}\n              </div>\n            </div>\n          {{/if}}\n        </div>\n\n      {{/if}}\n      </div>\n    {{/if}}\n  </template>\n}\n\n","import * as Mod1 from \"./discourse/connectors/static-page-after-content__success/static-page-after-content__success.gjs\";\nconst compatModules = {\n  \"discourse/connectors/static-page-after-content__success/static-page-after-content__success\": Mod1,\n};\nexport { compatModules };\nexport default compatModules;\n"],"names":["getObjectForTheme","ForumSuccessStats","Component","dt7948","g","this","prototype","service","currentUser","i","router","tracked","selectedPeriod","p90ViewMode","error","loadingP90","loadingSLA","loadingCoverage","loadingCommunityVsStaff","loadingSentiment","loadingQueue","loadingActivity","loadingGoals","loadingTTFRBySeverity","showCustomDates","startDate","endDate","chartLabels","p90ChartData","sev1ChartData","sev2ChartData","sev3ChartData","coverageSev1ChartData","coverageSev2ChartData","coverageSev3ChartData","communityVsStaffData_chart","sentimentOverallData","queueBugs","queueSelectedTab","queueSortOrder","dauChartData","engagedUsersChartData","dauMauRatioChartData","ttfrMetricType","rawP90Data","rawP50Data","rawAvgData","p90_ttfr","sla_compliance","coverage","community_vs_staff","sentiment","bug_queue","user_activity","goals","goals_7d","ttfr_by_severity","queryIds","queryCache","currentQueryGeneration","ttfrP90Overall","ttfrP50Overall","ttfrAvgOverall","goalSev1TTFR","goalOtherBugsTTFR","goalForumResponse","goalSev1Coverage","goal7dSev1TTFR","goal7dOtherBugsTTFR","goal7dForumResponse","goal7dSev1Coverage","sev1","current","target","sev2","sev3","slaData","ttfrBySeverityData","overall","bugs","features","other","coverageData","communityPercent","staffPercent","communityVsStaffData","excludingBugFeature","byCategory","name","score","sentimentData","queries","sql","constructor","super","arguments","checkAuthAndLoad","isCursorStaff","groups","some","group","staff","loadData","transitionTo","n","action","ensureSingleQuery","key","queryDef","existingQuery","findQueryByName","console","log","updateQuery","id","newQuery","createQuery","response","ajax","find","q","type","data","query","description","queryId","runQuery","params","cacheKey","JSON","stringify","loading","thisGeneration","now","moment","format","subtract","generateChartLabels","start_date","end_date","p90Params","category_filter","Date","toISOString","startTime","goalsQueryId","goalsResult","processGoalsData","next","err","goals7dQueryId","goals7dResult","processGoals7dData","p90QueryId","p90Result","processP90Data","slaQueryId","slaResult","processSLAData","ttfrBySevQueryId","ttfrBySevResult","processTTFRBySeverityData","coverageQueryId","coverageResult","processCoverageData","communityQueryId","communityResult","processCommunityVsStaffData","sentimentQueryId","sentimentResult","processSentimentData","queueQueryId","queueResult","processQueueData","activityQueryId","activityResult","processActivityData","popupAjaxError","start","end","labels","clone","isSameOrBefore","push","add","calculateTrailingPercentile","dataArray","trailingDays","percentile","length","trailing","Math","max","window","slice","sort","a","b","index","floor","round","applyP90ViewMode","rows","warn","Error","map","r","parseFloat","calcMedian","arr","sorted","mid","reduce","sum","val","calcAverage","updateTTFRChartData","aggregateDailyToWeekly","dailyData","weekly","numWeeks","weekStart","weekEnd","weekValues","filter","d","date","isSameOrAfter","isBefore","value","p90Index","dates","Set","sev1Data","sev2Data","sev3Data","sev1TotalTopics","sev1WithinSLA","sev2TotalTopics","sev2WithinSLA","sev3TotalTopics","sev3WithinSLA","forEach","sev1Overall","sev2Overall","sev3Overall","dateRows","sev1Agg","sev2Agg","sev3Agg","sev1Row","sev2Row","sev3Row","totalTopics","totalWithResponse","parseInt","overallPercentage","row","community","totalCommunity","totalStaff","total","latestScore","title","created_at","slug","severity","p90","createChartConfig","label","color","showYAxis","datasets","backgroundColor","borderColor","pointRadius","borderWidth","pointBackgroundColor","pointBorderColor","tension","options","plugins","legend","display","responsive","maintainAspectRatio","aspectRatio","scales","y","beginAtZero","grid","x","ticks","maxRotation","minRotation","createMultiLineChartConfig","ds","borderDash","position","p90ChartConfig","slaChartConfig","allData","coverageChartConfig","dataLength","splitPoint","segment","ctx","p0DataIndex","communityVsStaffChartConfig","sentimentChartConfig","dauMauChartConfig","dauEngagementChartConfig","dau","engaged","handlePeriodChange","event","period","changePeriod","selectTTFRMetric","metricType","selectQueueTab","tab","toggleQueueSort","filteredQueueBugs","filtered","sevMap","bug","dateA","dateB","toggleCustomDates","updateStartDate","updateEndDate","dateRangeLabel","goalSev1Status","goalOtherBugsStatus","goalForumResponseStatus","goalSev1CoverageStatus","periodDescriptionText","fortnight","month","periodShortText","isStaffOrCursorStaff","setComponentTemplate","createTemplateFactory","block","moduleName","scope","on","eq","fn","Chart","isStrictMode","compatModules"],"mappings":";AAEwBA,EAAkB;ACU3B,MAAMC,UAA0BC,SAAAC,OAAAC,EAAAC,KAAAC,yBAC5CC,GAAA,CAAAC,QAAAL,OAAAM,EAAAJ,KAAA;OAAAF,OAAAC,EAAAC,KAAAC,oBACAC,GAAA,CAAAG,QAAAP,OAAAM,EAAAJ,KAAA;OAAAF,OAAAC,EAAAC,KAAAC,4BACAK,GAAA,WAAA,MAAyB,OAAA,EAAA,CAAAC,QAAAT,OAAAM,EAAAJ,KAAA;OAAAF,OAAAC,EAAAC,KAAAC,yBACzBK,GAAA,WAAA,MAAsB,MAAA,EAAA,CAAAE,QAAAV,OAAAM,EAAAJ,KAAA;OAAAF,OAAAC,EAAAC,KAAAC,mBACtBK,GAAA,WAAA,OAAgB,IAAA,EAAA,CAAAG,QAAAX,OAAAM,EAAAJ,KAAA;OAAAF,OAAAC,EAAAC,KAAAC,wBAChBK,GAAA,WAAA,OAAqB,CAAA,EAAA,CAAAI,QAAAZ,OAAAM,EAAAJ,KAAA;OAAAF,OAAAC,EAAAC,KAAAC,wBACrBK,GAAA,WAAA,OAAqB,CAAA,EAAA,CAAAK,QAAAb,OAAAM,EAAAJ,KAAA;OAAAF,OAAAC,EAAAC,KAAAC,6BACrBK,GAAA,WAAA,OAA0B,CAAA,EAAA,CAAAM,QAAAd,OAAAM,EAAAJ,KAAA;OAAAF,OAAAC,EAAAC,KAAAC,qCAC1BK,GAAA,WAAA,OAAkC,CAAA,EAAA,CAAAO,QAAAf,OAAAM,EAAAJ,KAAA;OAAAF,OAAAC,EAAAC,KAAAC,8BAClCK,GAAA,WAAA,OAA2B,CAAA,EAAA,CAAAQ,QAAAhB,OAAAM,EAAAJ,KAAA;OAAAF,OAAAC,EAAAC,KAAAC,0BAC3BK,GAAA,WAAA,OAAuB,CAAA,EAAA,CAAAS,QAAAjB,OAAAM,EAAAJ,KAAA;OAAAF,OAAAC,EAAAC,KAAAC,6BACvBK,GAAA,WAAA,OAA0B,CAAA,EAAA,CAAAU,QAAAlB,OAAAM,EAAAJ,KAAA;OAAAF,OAAAC,EAAAC,KAAAC,0BAC1BK,GAAA,WAAA,OAAuB,CAAA,EAAA,CAAAW,QAAAnB,OAAAM,EAAAJ,KAAA;OAAAF,OAAAC,EAAAC,KAAAC,mCACvBK,GAAA,WAAA,OAAgC,CAAA,EAAA,CAAAY,QAAApB,OAAAM,EAAAJ,KAAA;OAAAF,OAAAC,EAAAC,KAAAC,6BAChCK,GAAA,WAAA,OAA0B,CAAA,EAAA,CAAAa,QAAArB,OAAAM,EAAAJ,KAAA;OAAAF,OAAAC,EAAAC,KAAAC,uBAC1BK,GAAA,WAAA,OAAoB,IAAA,EAAA,CAAAc,QAAAtB,OAAAM,EAAAJ,KAAA;OAAAF,OAAAC,EAAAC,KAAAC,qBACpBK,GAAA,WAAA,OAAkB,IAAA,EAAA,CAAAe,QAAAvB,OAAAM,EAAAJ,KAAA;OAAAF,OAAAC,EAAAC,KAAAC,yBAClBK,GAAA,WAAA,MAAsB,EAAE,EAAA,CAAAgB,QAAAxB,OAAAM,EAAAJ,KAAA;OAAAF,OAAAC,EAAAC,KAAAC,0BACxBK,GAAA,WAAA,MAAuB,EAAE,EAAA,CAAAiB,QAAAzB,OAAAM,EAAAJ,KAAA;OAAAF,OAAAC,EAAAC,KAAAC,2BACzBK,GAAA,WAAA,MAAwB,EAAE,EAAA,CAAAkB,QAAA1B,OAAAM,EAAAJ,KAAA;OAAAF,OAAAC,EAAAC,KAAAC,2BAC1BK,GAAA,WAAA,MAAwB,EAAE,EAAA,CAAAmB,QAAA3B,OAAAM,EAAAJ,KAAA;OAAAF,OAAAC,EAAAC,KAAAC,2BAC1BK,GAAA,WAAA,MAAwB,EAAE,EAAA,CAAAoB,QAAA5B,OAAAM,EAAAJ,KAAA;OAAAF,OAAAC,EAAAC,KAAAC,mCAC1BK,GAAA,WAAA,MAAgC,EAAE,EAAA,CAAAqB,QAAA7B,OAAAM,EAAAJ,KAAA;OAAAF,OAAAC,EAAAC,KAAAC,mCAClCK,GAAA,WAAA,MAAgC,EAAE,EAAA,CAAAsB,QAAA9B,OAAAM,EAAAJ,KAAA;OAAAF,OAAAC,EAAAC,KAAAC,mCAClCK,GAAA,WAAA,MAAgC,EAAE,EAAA,CAAAuB,QAAA/B,OAAAM,EAAAJ,KAAA;OAAAF,OAAAC,EAAAC,KAAAC,wCAClCK,GAAA,WAAA,MAAqC,EAAE,EAAA,CAAAwB,QAAAhC,OAAAM,EAAAJ,KAAA;OAAAF,OAAAC,EAAAC,KAAAC,kCACvCK,GAAA,WAAA,MAA+B,EAAE,EAAA,CAAAyB,QAAAjC,OAAAM,EAAAJ,KAAA;OAAAF,OAAAC,EAAAC,KAAAC,uBACjCK,GAAA,WAAA,MAAoB,EAAE,EAAA,CAAA0B,QAAAlC,OAAAM,EAAAJ,KAAA;OAAAF,OAAAC,EAAAC,KAAAC,8BACtBK,GAAA,WAAA,MAA2B,KAAA,EAAA,CAAA2B,QAAAnC,OAAAM,EAAAJ,KAAA;OAAAF,OAAAC,EAAAC,KAAAC,4BAC3BK,GAAA,WAAA,MAAyB,QAAA,EAAA,CAAA4B,QAAApC,OAAAM,EAAAJ,KAAA;OAAAF,OAAAC,EAAAC,KAAAC,0BACzBK,GAAA,WAAA,MAAuB,EAAE,EAAA,CAAA6B,QAAArC,OAAAM,EAAAJ,KAAA;OAAAF,OAAAC,EAAAC,KAAAC,mCACzBK,GAAA,WAAA,MAAgC,EAAE,EAAA,CAAA8B,QAAAtC,OAAAM,EAAAJ,KAAA;OAAAF,OAAAC,EAAAC,KAAAC,kCAClCK,GAAA,WAAA,MAA+B,EAAE,EAAA,CAAA+B,QAAAvC,OAAAM,EAAAJ,KAAA;OAAAF,OAAAC,EAAAC,KAAAC,4BACjCK,GAAA,WAAA,MAAyB,KAAA,EAAA,CAAAgC,QAAAxC,OAAAM,EAAAJ,KAAA;AAG1BuC,WAAa;AACbC,WAAa;AACbC,WAAa;OAAG3C,OAAAC,EAAAC,KAAAC,sBACfK,GAAA,WAAA,MAAmB,CAClBoC,SAAU,KACVC,eAAgB,KAChBC,SAAU,KACVC,mBAAoB,KACpBC,UAAW,KACXC,UAAW,KACXC,cAAe,KACfC,MAAO,KACPC,SAAU,KACVC,iBAAkB,KACpB,EAAA,CAAAC,QAAAtD,OAAAM,EAAAJ,KAAA;AAGAqD,WAAa,CAAA;AAGbC,uBAAyB;OAEzBxD,OAAAC,EAAAC,KAAAC,4BACCK,GAAA,WAAA,OAAyB,CAAA,EAAA,CAAAiD,QAAAzD,OAAAM,EAAAJ,KAAA;OAAAF,OAAAC,EAAAC,KAAAC,4BACzBK,GAAA,WAAA,OAAyB,CAAA,EAAA,CAAAkD,QAAA1D,OAAAM,EAAAJ,KAAA;OAAAF,OAAAC,EAAAC,KAAAC,4BACzBK,GAAA,WAAA,OAAyB,CAAA,EAAA,CAAAmD,QAAA3D,OAAAM,EAAAJ,KAAA;OAAAF,OAAAC,EAAAC,KAAAC,0BAGzBK,GAAA,WAAA,OAAuB,CAAA,EAAA,CAAAoD,QAAA5D,OAAAM,EAAAJ,KAAA;OADxBF,OAAAC,EAAAC,KAAAC,+BAECK,GAAA,WAAA,OAA4B,CAAA,EAAA,CAAAqD,QAAA7D,OAAAM,EAAAJ,KAAA;OAAAF,OAAAC,EAAAC,KAAAC,+BAC5BK,GAAA,WAAA,OAA4B,CAAA,EAAA,CAAAsD,QAAA9D,OAAAM,EAAAJ,KAAA;OAAAF,OAAAC,EAAAC,KAAAC,8BAC5BK,GAAA,WAAA,OAA2B,CAAA,EAAA,CAAAuD,QAAA/D,OAAAM,EAAAJ,KAAA;OAAAF,OAAAC,EAAAC,KAAAC,4BAG3BK,GAAA,WAAA,OAAyB,CAAA,EAAA,CAAAwD,QAAAhE,OAAAM,EAAAJ,KAAA;OAD1BF,OAAAC,EAAAC,KAAAC,iCAECK,GAAA,WAAA,OAA8B,CAAA,EAAA,CAAAyD,QAAAjE,OAAAM,EAAAJ,KAAA;OAAAF,OAAAC,EAAAC,KAAAC,iCAC9BK,GAAA,WAAA,OAA8B,CAAA,EAAA,CAAA0D,QAAAlE,OAAAM,EAAAJ,KAAA;OAAAF,OAAAC,EAAAC,KAAAC,gCAC9BK,GAAA,WAAA,OAA6B,CAAA,EAAA,CAAA2D,QAAAnE,OAAAM,EAAAJ,KAAA;OAAAF,OAAAC,EAAAC,KAAAC,qBAE7BK,GAAA,WAAA,MAAkB,CACjB4D,KAAM,CAAEC,QAAS,GAAIC,OAAQ,IAC7BC,KAAM,CAAEF,QAAS,GAAIC,OAAQ,IAC7BE,KAAM,CAAEH,QAAS,GAAIC,OAAQ,IAC/B,EAAA,CAAAG,QAAAzE,OAAAM,EAAAJ,KAAA;OAAAF,OAAAC,EAAAC,KAAAC,gCACCK,GAAA,WAAA,MAA6B,CAC5B4D,KAAM,CAAEC,QAAS,GACjBE,KAAM,CAAEF,QAAS,GACjBG,KAAM,CAAEH,QAAS,GACnB,EAAA,CAAAK,QAAA1E,OAAAM,EAAAJ,KAAA;OAAAF,OAAAC,EAAAC,KAAAC,0BACCK,GAAA,WAAA,MAAuB,CACtBmE,QAAS,GACTP,KAAM,IACNG,KAAM,GACNC,KAAM,GACNI,KAAM,GACNC,SAAU,GACVC,MAAO,GACT,EAAA,CAAAC,QAAA/E,OAAAM,EAAAJ,KAAA;OAAAF,OAAAC,EAAAC,KAAAC,kCACCK,GAAA,WAAA,MAA+B,CAC9BwE,iBAAkB,EAClBC,aAAc,EAChB,EAAA,CAAAC,QAAAlF,OAAAM,EAAAJ,KAAA;OAAAF,OAAAC,EAAAC,KAAAC,2BACCK,GAAA,WAAA,MAAwB,CACvBmE,QAAS,IACTQ,oBAAqB,IACrBC,WAAY,CACV,CAAEC,KAAM,UAAWC,MAAO,KAC1B,CAAED,KAAM,UAAWC,MAAO,KAC1B,CAAED,KAAM,OAAQC,MAAO,KACvB,CAAED,KAAM,WAAYC,MAAO,MAE/B,EAAA,CAAAC,QAAAvF,OAAAM,EAAAJ,KAAA;AAGAsF,QAAU,CACR5C,SAAU,CACRyC,KAAM,+BACNI,IAAK,ksCAmCP5C,eAAgB,CACdwC,KAAM,iCACNI,IAAK,ksEAgEP3C,SAAU,CACRuC,KAAM,0CACNI,IAAK,mjEAqEP1C,mBAAoB,CAClBsC,KAAM,6CACNI,IAAK,svCAiCPzC,UAAW,CACTqC,KAAM,qCACNI,IAAK,i0BA6BPxC,UAAW,CACToC,KAAM,qCACNI,IAAK,6iDAyDPvC,cAAe,CACbmC,KAAM,gCACNI,IAAK,+6DAmDPtC,MAAO,CACLkC,KAAM,wBACNI,IAAK,+4HA2HPrC,SAAU,CACRiC,KAAM,2BACNI,IAAK,4pIA+HPpC,iBAAkB,CAChBgC,KAAM,mCACNI,IAAK;AA8DTC,WAAAA,GACEC,SAASC,WACT1F,KAAK2F,kBACP,CAEA,sBACMA,GAEJ,MAAMC,EAAgB5F,KAAKG,aAAa0F,QAAQC,KAAKC,GAAwB,gBAAfA,EAAMZ;AAC/DnF,KAAKG,cAAiBH,KAAKG,YAAY6F,OAAUJ,GAItD5F,KAAKiG,WAHHjG,KAAKK,OAAO6F,aAAa,IAI7B,QAAApG,OAAAqG,EAAAnG,KAAAC,8BATCmG,GAAA,CAWD,uBAAMC,CAAkBC,GACtB,IACE,MAAMC,EAAWvG,KAAKsF,QAAQgB,GACxBE,QAAsBxG,KAAKyG,gBAAgBF,EAASpB;AAE1D,GAAIqB,EAEFE,QAAQC,IAAI,kBAAkBL,wBACxBtG,KAAK4G,YAAYJ,EAAcK,GAAIN,GACzCvG,KAAKoD,SAASkD,GAAOE,EAAcK;IAC9B,CACLH,QAAQC,IAAI,sBAAsBL;AAClC,MAAMQ,QAAiB9G,KAAK+G,YAAYR;AACxCvG,KAAKoD,SAASkD,GAAOQ,EAASD,EAChC,CACA,OAAO7G,KAAKoD,SAASkD,EACvB,CAAE,MAAO7F,GAEP,MADAiG,QAAQjG,MAAM,wBAAwB6F,KAAQ7F,GACxCA,CACR,CACF,CAEA,qBAAMgG,CAAgBtB,GACpB,IACE,MAAM6B,QAAiBC,EAAK;AAC5B,OAAOD,EAAS1B,SAAS4B,KAAKC,GAAKA,EAAEhC,OAASA,EAChD,CAAE,MAAO1E,GAEP,OADAiG,QAAQjG,MAAM,uBAAwBA,GAC/B,IACT,CACF,CAEA,iBAAMsG,CAAYR,GAChB,IAWE,aAVuBU,EAAK,kCAAmC,CAC7DG,KAAM,OACNC,KAAM,CACJC,MAAO,CACLnC,KAAMoB,EAASpB,KACfI,IAAKgB,EAAShB,IACdgC,YAAa,oDAIHD,KAClB,CAAE,MAAO7G,GAEP,MADAiG,QAAQjG,MAAM,wBAAyBA,GACjCA,CACR,CACF,CAEA,iBAAMmG,CAAYY,EAASjB,GACzB,IAWE,aAVuBU,EAAK,mCAAmCO,IAAW,CACxEJ,KAAM,MACNC,KAAM,CACJC,MAAO,CACLnC,KAAMoB,EAASpB,KACfI,IAAKgB,EAAShB,IACdgC,YAAa,oDAIHD,KAClB,CAAE,MAAO7G,GAEP,MADAiG,QAAQjG,MAAM,wBAAyBA,GACjCA,CACR,CACF,CAEA,cAAMgH,CAASD,EAASE,EAAS,IAE/B,MAAMC,EAAW,GAAGH,KAAWI,KAAKC,UAAUH;AAG9C,GAAI1H,KAAKqD,WAAWsE,GAElB,OADAjB,QAAQC,IAAI,iCAAiCa,KACtCxH,KAAKqD,WAAWsE;AAGzB,IACE,MAAMX,QAAiBC,EAAK,mCAAmCO,QAAe,CAC5EJ,KAAM,OACNC,KAAM,CACJK,OAAQE,KAAKC,UAAUH;AAQ3B,OAHA1H,KAAKqD,WAAWsE,GAAYX,EAC5BN,QAAQC,IAAI,2BAA2Ba,KAEhCR,CACT,CAAE,MAAOvG,GAEP,MADAiG,QAAQjG,MAAM,uBAAuB+G,KAAY/G,GAC3CA,CACR,CACF,CAEA,cACMwF,GACJjG,KAAK8H,SAAU,EACf9H,KAAKS,MAAQ,KAGbT,KAAKsD;AACL,MAAMyE,EAAiB/H,KAAKsD;AAC5BoD,QAAQC,IAAI,gCAAgCoB;AAE5C,IAEE,IAAI3G,EAAWC;AAEf,GAAIrB,KAAKoB,WAAapB,KAAKqB,QACzBD,EAAYpB,KAAKoB,UACjBC,EAAUrB,KAAKqB;IACV,CAEL,MAAM2G,EAAMC;AAGZ,OAFA5G,EAAU2G,EAAIE,OAAO,uBAEblI,KAAKO,gBACX,IAAK,SACHa,EAAY6G,SAASE,SAAS,EAAG,QAAQD,OAAO,cAChD7G,EAAU2G,EAAIE,OAAO;AACrB;AACF,IAAK,YACH9G,EAAY6G,SAASE,SAAS,GAAI,QAAQD,OAAO,cACjD7G,EAAU2G,EAAIE,OAAO;AACrB;AACF,IAAK,QAYL,QACE9G,EAAY6G,SAASE,SAAS,GAAI,QAAQD,OAAO,cACjD7G,EAAU2G,EAAIE,OAAO;;AAVvB,IAAK,UACH9G,EAAY6G,SAASE,SAAS,EAAG,UAAUD,OAAO,cAClD7G,EAAU2G,EAAIE,OAAO;AACrB;AACF,IAAK,WACH9G,EAAY6G,SAASE,SAAS,GAAI,UAAUD,OAAO,cACnD7G,EAAU2G,EAAIE,OAAO,cAM3B,CAGAlI,KAAKoI,oBAAoBhH,EAAWC;AAEpC,MAAMqG,EAAS,CACbW,WAAYjH,EACZkH,SAAUjH,GAINkH,EAAY,IACbb,EACHc,gBAAiB;AAInB9B,QAAQC,IAAI,qCAAqC,IAAI8B,MAAOC;AAC5D,MAAMC,EAAYF,KAAKT;AAGvB,IACE,MAAMY,QAAqB5I,KAAKqG,kBAAkB,SAC5CwC,QAAoB7I,KAAKyH,SAASmB,EAAc,CAAA;AACtDlC,QAAQC,IAAI,sBAAsB8B,KAAKT,MAAQW,OAC3CZ,IAAmB/H,KAAKsD,wBAC1BtD,KAAK8I,iBAAiBD,GACtBE,EAAK,IAAM/I,KAAKiB,cAAe,IAE/ByF,QAAQC,IAAI,6CAA6CoB,iBAA8B/H,KAAKsD,yBAEhG,CAAE,MAAO0F,GACPtC,QAAQjG,MAAM,eAAgBuI,GAC1BjB,IAAmB/H,KAAKsD,wBAC1ByF,EAAK,IAAM/I,KAAKiB,cAAe,EAEnC,CAGA,IACE,MAAMgI,QAAuBjJ,KAAKqG,kBAAkB,YAC9C6C,QAAsBlJ,KAAKyH,SAASwB,EAAgB,CAAA;AAC1DvC,QAAQC,IAAI,yBAAyB8B,KAAKT,MAAQW,OAC9CZ,IAAmB/H,KAAKsD,uBAC1BtD,KAAKmJ,mBAAmBD,GAExBxC,QAAQC,IAAI,gDAAgDoB,IAEhE,CAAE,MAAOiB,GACPtC,QAAQjG,MAAM,kBAAmBuI,EACnC,CAGA,IACE,MAAMI,QAAmBpJ,KAAKqG,kBAAkB,YAC1CgD,QAAkBrJ,KAAKyH,SAAS2B,EAAYb;AAClD7B,QAAQC,IAAI,oBAAoB8B,KAAKT,MAAQW,OACzCZ,IAAmB/H,KAAKsD,wBAC1BtD,KAAKsJ,eAAeD,GACpBN,EAAK,IAAM/I,KAAKU,YAAa,IAE7BgG,QAAQC,IAAI,2CAA2CoB,IAE3D,CAAE,MAAOiB,GACPtC,QAAQjG,MAAM,aAAcuI,GACxBjB,IAAmB/H,KAAKsD,wBAC1ByF,EAAK,IAAM/I,KAAKU,YAAa,EAEjC,CAGA,IACE,MAAM6I,QAAmBvJ,KAAKqG,kBAAkB,kBAC1CmD,QAAkBxJ,KAAKyH,SAAS8B,EAAY7B;AAClDhB,QAAQC,IAAI,oBAAoB8B,KAAKT,MAAQW,OACzCZ,IAAmB/H,KAAKsD,wBAC1BtD,KAAKyJ,eAAeD,GACpBT,EAAK,IAAM/I,KAAKW,YAAa,IAE7B+F,QAAQC,IAAI,2CAA2CoB,IAE3D,CAAE,MAAOiB,GACPtC,QAAQjG,MAAM,aAAcuI,GACxBjB,IAAmB/H,KAAKsD,wBAC1ByF,EAAK,IAAM/I,KAAKW,YAAa,EAEjC,CAGA,IACE,MAAM+I,QAAyB1J,KAAKqG,kBAAkB,oBAChDsD,QAAwB3J,KAAKyH,SAASiC,EAAkBhC;AAC9DhB,QAAQC,IAAI,iCAAiC8B,KAAKT,MAAQW,OACtDZ,IAAmB/H,KAAKsD,wBAC1BtD,KAAK4J,0BAA0BD,GAC/BZ,EAAK,IAAM/I,KAAKkB,uBAAwB,IAExCwF,QAAQC,IAAI,wDAAwDoB,IAExE,CAAE,MAAOiB,GACPtC,QAAQjG,MAAM,0BAA2BuI,GACrCjB,IAAmB/H,KAAKsD,wBAC1ByF,EAAK,IAAM/I,KAAKkB,uBAAwB,EAE5C,CAGA,IACE,MAAM2I,QAAwB7J,KAAKqG,kBAAkB,YAC/CyD,QAAuB9J,KAAKyH,SAASoC,EAAiBnC;AAC5DhB,QAAQC,IAAI,yBAAyB8B,KAAKT,MAAQW,OAC9CZ,IAAmB/H,KAAKsD,wBAC1BtD,KAAK+J,oBAAoBD,GACzBf,EAAK,IAAM/I,KAAKY,iBAAkB,IAElC8F,QAAQC,IAAI,gDAAgDoB,IAEhE,CAAE,MAAOiB,GACPtC,QAAQjG,MAAM,kBAAmBuI,GAC7BjB,IAAmB/H,KAAKsD,wBAC1ByF,EAAK,IAAM/I,KAAKY,iBAAkB,EAEtC,CAGA,IACE,MAAMoJ,QAAyBhK,KAAKqG,kBAAkB,sBAChD4D,QAAwBjK,KAAKyH,SAASuC,EAAkBtC;AAC9DhB,QAAQC,IAAI,mCAAmC8B,KAAKT,MAAQW,OACxDZ,IAAmB/H,KAAKsD,wBAC1BtD,KAAKkK,4BAA4BD,GACjClB,EAAK,IAAM/I,KAAKa,yBAA0B,IAE1C6F,QAAQC,IAAI,0DAA0DoB,IAE1E,CAAE,MAAOiB,GACPtC,QAAQjG,MAAM,4BAA6BuI,GACvCjB,IAAmB/H,KAAKsD,wBAC1ByF,EAAK,IAAM/I,KAAKa,yBAA0B,EAE9C,CAGA,IACE,MAAMsJ,QAAyBnK,KAAKqG,kBAAkB,aAChD+D,QAAwBpK,KAAKyH,SAAS0C,EAAkBzC;AAC9DhB,QAAQC,IAAI,0BAA0B8B,KAAKT,MAAQW,OAC/CZ,IAAmB/H,KAAKsD,wBAC1BtD,KAAKqK,qBAAqBD,GAC1BrB,EAAK,IAAM/I,KAAKc,kBAAmB,IAEnC4F,QAAQC,IAAI,iDAAiDoB,IAEjE,CAAE,MAAOiB,GACPtC,QAAQjG,MAAM,mBAAoBuI,GAC9BjB,IAAmB/H,KAAKsD,wBAC1ByF,EAAK,IAAM/I,KAAKc,kBAAmB,EAEvC,CAGA,IACE,MAAMwJ,QAAqBtK,KAAKqG,kBAAkB,aAC5CkE,QAAoBvK,KAAKyH,SAAS6C,EAAc5C;AACtDhB,QAAQC,IAAI,sBAAsB8B,KAAKT,MAAQW,OAC3CZ,IAAmB/H,KAAKsD,wBAC1BtD,KAAKwK,iBAAiBD,GACtBxB,EAAK,IAAM/I,KAAKe,cAAe,IAE/B2F,QAAQC,IAAI,6CAA6CoB,IAE7D,CAAE,MAAOiB,GACPtC,QAAQjG,MAAM,eAAgBuI,GAC1BjB,IAAmB/H,KAAKsD,wBAC1ByF,EAAK,IAAM/I,KAAKe,cAAe,EAEnC,CAGA,IACE,MAAM0J,QAAwBzK,KAAKqG,kBAAkB,iBAC/CqE,QAAuB1K,KAAKyH,SAASgD,EAAiB/C;AAC5DhB,QAAQC,IAAI,yBAAyB8B,KAAKT,MAAQW,OAC9CZ,IAAmB/H,KAAKsD,wBAC1BtD,KAAK2K,oBAAoBD,GACzB3B,EAAK,IAAM/I,KAAKgB,iBAAkB,IAElC0F,QAAQC,IAAI,gDAAgDoB,IAEhE,CAAE,MAAOiB,GACPtC,QAAQjG,MAAM,kBAAmBuI,GAC7BjB,IAAmB/H,KAAKsD,wBAC1ByF,EAAK,IAAM/I,KAAKgB,iBAAkB,EAEtC,CAEA0F,QAAQC,IAAI,4BAA4B8B,KAAKT,MAAQW,MACvD,CAAE,MAAOlI,GACPiG,QAAQjG,MAAM,+BAAgCA,GAC9CT,KAAKS,MAAQ,2FACbmK,EAAenK,EACjB,CACF,QAAAX,OAAAqG,EAAAnG,KAAAC,sBAzPCmG,GAAA,CA2PDgC,mBAAAA,CAAoBhH,EAAWC,GAC7B,MAAMwJ,EAAQ5C,OAAO7G,GACf0J,EAAM7C,OAAO5G,GAEb0J,EAAS;AAGf,IAAI5G,EAAU0G,EAAMG;AACpB,KAAO7G,EAAQ8G,eAAeH,IAC5BC,EAAOG,KAAK/G,EAAQ+D,OAAO,UAC3B/D,EAAQgH,IAAI,EAAG;AAGjBnL,KAAKsB,YAAcyJ,CACrB,CAEAK,2BAAAA,CAA4BC,EAAWC,EAAcC,EAAa,IAChE,GAAyB,IAArBF,EAAUG,OAAc,MAAO;AAEnC,MAAMC,EAAW;AACjB,IAAK,IAAIrL,EAAI,EAAGA,EAAIiL,EAAUG,OAAQpL,IAAK,CACzC,MAAMyK,EAAQa,KAAKC,IAAI,EAAGvL,GAAKkL,EAAe,IACxCM,EAASP,EAAUQ,MAAMhB,EAAOzK,EAAI,GAAG0L,KAAK,CAACC,EAAGC,IAAMD,EAAIC,GAC1DC,EAAQP,KAAKQ,MAAMN,EAAOJ,OAASD;AACzCE,EAASP,KAAKQ,KAAKS,MAAsB,GAAhBP,EAAOK,IAAe,GACjD,CACA,OAAOR,CACT,CAEAW,gBAAAA,GAE2B,SAArBpM,KAAKQ,YAEPR,KAAKuB,aAAe,IAAIvB,KAAKuC,YACC,SAArBvC,KAAKQ,YAEdR,KAAKuB,aAAevB,KAAKoL,4BAA4BpL,KAAKuC,WAAY,GACxC,UAArBvC,KAAKQ,cAEdR,KAAKuB,aAAevB,KAAKoL,4BAA4BpL,KAAKuC,WAAY,IAE1E,CAEA+G,cAAAA,CAAetC,GAEb,GADAN,QAAQC,IAAI,iBAAkBK,IACzBA,IAAaA,EAASqF,MAAiC,IAAzBrF,EAASqF,KAAKb,OAE/C,MADA9E,QAAQ4F,KAAK,0BACP,IAAIC,MAAM;AAGlB7F,QAAQC,IAAI,+BAAgCK,EAASqF,KAAKb;AAC1D,MAAMa,EAAOrF,EAASqF;AAGtBrM,KAAKuC,WAAa8J,EAAKG,IAAIC,GAAKC,WAAWD,EAAE,KAAO,GACpDzM,KAAKwC,WAAa6J,EAAKG,IAAIC,GAAKC,WAAWD,EAAE,KAAO,GACpDzM,KAAKyC,WAAa4J,EAAKG,IAAIC,GAAKC,WAAWD,EAAE,KAAO;AAGpD,MAAME,EAAcC,IAClB,IAAKA,GAAsB,IAAfA,EAAIpB,OAAc,OAAO;AACrC,MAAMqB,EAAS,IAAID,GAAKd,KAAK,CAACC,EAAGC,IAAMD,EAAIC,GACrCc,EAAMpB,KAAKQ,MAAMW,EAAOrB,OAAS;AACvC,OAAOqB,EAAOrB,OAAS,GAAM,GACxBqB,EAAOC,EAAM,GAAKD,EAAOC,IAAQ,EAClCD,EAAOC;AASb9M,KAAKuD,eAAiBmI,KAAKS,MAAoC,GAA9BQ,EAAW3M,KAAKuC,aAAoB,GACrEvC,KAAKwD,eAAiBkI,KAAKS,MAAoC,GAA9BQ,EAAW3M,KAAKwC,aAAoB,GACrExC,KAAKyD,eAAiBiI,KAAKS,MAAqC,GAR3CS,KACnB,IAAKA,GAAsB,IAAfA,EAAIpB,OAAc,OAAO;AAErC,OADYoB,EAAIG,OAAO,CAACC,EAAKC,IAAQD,GAAON,WAAWO,IAAQ,GAAI,GACtDL,EAAIpB,QAKc0B,CAAYlN,KAAKyC,aAAoB,GAGtEzC,KAAKmN,qBACP,CAEAA,mBAAAA,GAE8B,QAAxBnN,KAAKsC,eACPtC,KAAKuB,aAAe,IAAIvB,KAAKuC,YACI,QAAxBvC,KAAKsC,eACdtC,KAAKuB,aAAe,IAAIvB,KAAKwC,YACI,YAAxBxC,KAAKsC,iBACdtC,KAAKuB,aAAe,IAAIvB,KAAKyC,YAEjC,CAEA2K,sBAAAA,CAAuBC,EAAWjM,GAChC,GAAyB,IAArBiM,EAAU7B,OAAc,MAAO;AAEnC,MAAM8B,EAAS,GACTC,EAAWvN,KAAKsB,YAAYkK,OAC5BX,EAAQ5C,OAAO7G;AAErB,IAAK,IAAIhB,EAAI,EAAGA,EAAImN,EAAUnN,IAAK,CACjC,MAAMoN,EAAY3C,EAAMG,QAAQG,IAAI/K,EAAG,SACjCqN,EAAUD,EAAUxC,QAAQG,IAAI,EAAG,QAGnCuC,EAAaL,EAChBM,OAAOC,IACN,MAAMC,EAAO5F,OAAO2F,EAAEC;AACtB,OAAOA,EAAKC,cAAcN,IAAcK,EAAKE,SAASN,KAEvDjB,IAAIoB,GAAKA,EAAEI;AAEd,GAAIN,EAAWlC,OAAS,EAAG,CAEzB,MAAMqB,EAASa,EAAW5B,KAAK,CAACC,EAAGC,IAAMD,EAAIC,GACvCiC,EAAWvC,KAAKQ,MAAsB,GAAhBW,EAAOrB;AACnC8B,EAAOpC,KAAK2B,EAAOoB,IAAapB,EAAOA,EAAOrB,OAAS,GACzD,MACE8B,EAAOpC,KAAK,EAEhB,CAEA,OAAOoC,CACT,CAEA7D,cAAAA,CAAezC,GACb,IAAKA,IAAaA,EAASqF,MAAiC,IAAzBrF,EAASqF,KAAKb,OAE/C,MADA9E,QAAQ4F,KAAK,oCACP,IAAIC,MAAM;AAGlB,MAAMF,EAAOrF,EAASqF,KAChB6B,EAAQ,IAAI,IAAIC,IAAI9B,EAAKG,IAAIC,GAAKA,EAAE,MAGpC2B,EAAW,GACXC,EAAW,GACXC,EAAW;AAGjB,IAAIC,EAAkB,EAAGC,EAAgB,EACrCC,EAAkB,EAAGC,EAAgB,EACrCC,EAAkB,EAAGC,EAAgB;AAEzCV,EAAMW,QAAQhB,IACZ,MAAM3J,EAAOmI,EAAKnF,KAAKuF,GAAKA,EAAE,KAAOoB,GAAiB,IAATpB,EAAE,IACzCpI,EAAOgI,EAAKnF,KAAKuF,GAAKA,EAAE,KAAOoB,GAAiB,IAATpB,EAAE,IACzCnI,EAAO+H,EAAKnF,KAAKuF,GAAKA,EAAE,KAAOoB,GAAiB,IAATpB,EAAE;AAE/C2B,EAASlD,KAAKhH,IAAO,IAAM,GAC3BmK,EAASnD,KAAK7G,IAAO,IAAM,GAC3BiK,EAASpD,KAAK5G,IAAO,IAAM,GAGvBJ,IACFqK,GAAmBrK,EAAK,IAAM,EAC9BsK,GAAiBtK,EAAK,IAAM,GAE1BG,IACFoK,GAAmBpK,EAAK,IAAM,EAC9BqK,GAAiBrK,EAAK,IAAM,GAE1BC,IACFqK,GAAmBrK,EAAK,IAAM,EAC9BsK,GAAiBtK,EAAK,IAAM,KAKhCtE,KAAKwB,cAAgB4M,EACrBpO,KAAKyB,cAAgB4M,EACrBrO,KAAK0B,cAAgB4M;AAGrB,MAAMQ,EAAcP,EAAkB,EAClC7C,KAAKS,MAAOqC,EAAgBD,EAAmB,KAC/C,EACEQ,EAAcN,EAAkB,EAClC/C,KAAKS,MAAOuC,EAAgBD,EAAmB,KAC/C,EACEO,EAAcL,EAAkB,EAClCjD,KAAKS,MAAOyC,EAAgBD,EAAmB,KAC/C;AAEJ3O,KAAKuE,QAAU,CACbL,KAAM,CAAEC,QAAS2K,EAAa1K,OAAQ,IACtCC,KAAM,CAAEF,QAAS4K,EAAa3K,OAAQ,IACtCE,KAAM,CAAEH,QAAS6K,EAAa5K,OAAQ,IAE1C,CAEA2F,mBAAAA,CAAoB/C,GAClB,IAAKA,IAAaA,EAASqF,MAAiC,IAAzBrF,EAASqF,KAAKb,OAE/C,MADA9E,QAAQ4F,KAAK,8BACP,IAAIC,MAAM;AAGlB,MAAMF,EAAOrF,EAASqF,KAGhB4C,EAAW5C,EAAKsB,OAAOlB,GAAc,OAATA,EAAE,IAC9ByB,EAAQ,IAAI,IAAIC,IAAIc,EAASzC,IAAIC,GAAKA,EAAE,MAGxCyC,EAAU7C,EAAKnF,KAAKuF,GAAc,OAATA,EAAE,IAAwB,IAATA,EAAE,IAC5C0C,EAAU9C,EAAKnF,KAAKuF,GAAc,OAATA,EAAE,IAAwB,IAATA,EAAE,IAC5C2C,EAAU/C,EAAKnF,KAAKuF,GAAc,OAATA,EAAE,IAAwB,IAATA,EAAE,IAG5C2B,EAAW,GACXC,EAAW,GACXC,EAAW;AAEjBJ,EAAMW,QAAQhB,IACZ,MAAMwB,EAAUJ,EAAS/H,KAAKuF,GAAKA,EAAE,KAAOoB,GAAiB,IAATpB,EAAE,IAChD6C,EAAUL,EAAS/H,KAAKuF,GAAKA,EAAE,KAAOoB,GAAiB,IAATpB,EAAE,IAChD8C,EAAUN,EAAS/H,KAAKuF,GAAKA,EAAE,KAAOoB,GAAiB,IAATpB,EAAE;AAEtD2B,EAASlD,KAAKwB,WAAW2C,IAAU,KAAO,GAC1ChB,EAASnD,KAAKwB,WAAW4C,IAAU,KAAO,GAC1ChB,EAASpD,KAAKwB,WAAW6C,IAAU,KAAO,KAI5CvP,KAAK2B,sBAAwByM,EAC7BpO,KAAK4B,sBAAwByM,EAC7BrO,KAAK6B,sBAAwByM;AAG7B,IAAIkB,EAAc,EACdC,EAAoB;AAExBR,EAASJ,QAAQpC,IACf+C,GAAeE,SAASjD,EAAE,KAAO,EACjCgD,GAAqBC,SAASjD,EAAE,KAAO;AAGzC,MAAMkD,EAAoBH,EAAc,EACpC9D,KAAKS,MAAOsD,EAAoBD,EAAe,KAC/C;AAEJxP,KAAK6E,aAAe,CAClBJ,QAASkL,EACTzL,KAAMwH,KAAKS,MAAMO,WAAWwC,IAAU,KAAO,GAC7C7K,KAAMqH,KAAKS,MAAMO,WAAWyC,IAAU,KAAO,GAC7C7K,KAAMoH,KAAKS,MAAMO,WAAW0C,IAAU,KAAO,GAEjD,CAEAlF,2BAAAA,CAA4BlD,GAC1B,IAAKA,IAAaA,EAASqF,MAAiC,IAAzBrF,EAASqF,KAAKb,OAE/C,MADA9E,QAAQ4F,KAAK,wCACP,IAAIC,MAAM;AAGlB,MAAMF,EAAOrF,EAASqF;AAStB,GANArM,KAAK8B,2BAA6BuK,EAAKG,IAAIoD,KACzCC,UAAWH,SAASE,EAAI,KAAO,EAC/B5J,MAAO0J,SAASE,EAAI,KAAO,KAIkB,IAA3C5P,KAAK8B,2BAA2B0J,OAClC,MAAM,IAAIe,MAAM;AAGlB,MAAMuD,EAAiB9P,KAAK8B,2BAA2BiL,OAAO,CAACC,EAAKY,IAAMZ,GAAOY,EAAEiC,WAAa,GAAI,GAC9FE,EAAa/P,KAAK8B,2BAA2BiL,OAAO,CAACC,EAAKY,IAAMZ,GAAOY,EAAE5H,OAAS,GAAI,GACtFgK,EAAQF,EAAiBC;AAE/B/P,KAAKgF,qBAAuB,CAC1BF,iBAAkBkL,EAAQ,EAAItE,KAAKS,MAAO2D,EAAiBE,EAAS,KAAO,EAC3EjL,aAAciL,EAAQ,EAAItE,KAAKS,MAAO4D,EAAaC,EAAS,KAAO,EAEvE,CAEA3F,oBAAAA,CAAqBrD,GACnB,IAAKA,IAAaA,EAASqF,MAAiC,IAAzBrF,EAASqF,KAAKb,OAE/C,MADA9E,QAAQ4F,KAAK,+BACP,IAAIC,MAAM;AAGlB,MAAMF,EAAOrF,EAASqF;AAGtBrM,KAAK+B,qBAAuBsK,EAAKG,IAAIC,GAAKA,EAAE,IAAM;AAElD,MAAMwD,EAAc5D,EAAKb,OAAS,EAAIa,EAAKA,EAAKb,OAAS,GAAG,GAAK;AACjExL,KAAKqF,cAAgB,CACnBZ,QAASwL,EACThL,oBAAqBgL,EACrB/K,WAAY,GAEhB,CAEAsF,gBAAAA,CAAiBxD,GACf,IAAKA,IAAaA,EAASqF,KAGzB,OAFA3F,QAAQ4F,KAAK,gCACbtM,KAAKgC,UAAY;AAInB,MAAMqK,EAAOrF,EAASqF;AAGtBrM,KAAKgC,UAAYqK,EAAKG,IAAIoD,KACxB/I,GAAI+I,EAAI,GACRM,MAAON,EAAI,GACXO,WAAYP,EAAI,GAChBQ,KAAMR,EAAI,GACVS,SAAUT,EAAI,KAElB,CAEAjF,mBAAAA,CAAoB3D,GAClB,IAAKA,IAAaA,EAASqF,MAAiC,IAAzBrF,EAASqF,KAAKb,OAK/C,OAJA9E,QAAQ4F,KAAK,8BACbtM,KAAKmC,aAAe,GACpBnC,KAAKoC,sBAAwB,QAC7BpC,KAAKqC,qBAAuB;AAI9B,MAAMgK,EAAOrF,EAASqF;AAItBrM,KAAKmC,aAAekK,EAAKG,IAAIoD,GAAOF,SAASE,EAAI,KAAO,GACxD5P,KAAKoC,sBAAwBiK,EAAKG,IAAIoD,GAAOF,SAASE,EAAI,KAAO,GACjE5P,KAAKqC,qBAAuBgK,EAAKG,IAAIoD,GAAOlD,WAAWkD,EAAI,KAAO,EACpE,CAEA9G,gBAAAA,CAAiB9B,GACf,IAAKA,IAAaA,EAASqF,MAAiC,IAAzBrF,EAASqF,KAAKb,OAE/C,YADA9E,QAAQ4F,KAAK;AAIf,MAAMsD,EAAM5I,EAASqF,KAAK;AAE1BrM,KAAK0D,aAAegI,KAAKS,MAAkC,IAA3BO,WAAWkD,EAAI,KAAO,IAAW,GACjE5P,KAAK2D,kBAAoB+H,KAAKS,MAAkC,IAA3BO,WAAWkD,EAAI,KAAO,IAAW,GACtE5P,KAAK4D,kBAAoB8H,KAAKS,MAAkC,IAA3BO,WAAWkD,EAAI,KAAO,IAAW,GACtE5P,KAAK6D,iBAAmB6H,KAAKS,MAAkC,IAA3BO,WAAWkD,EAAI,KAAO,IAAW,EACvE,CAEAzG,kBAAAA,CAAmBnC,GACjB,IAAKA,IAAaA,EAASqF,MAAiC,IAAzBrF,EAASqF,KAAKb,OAE/C,YADA9E,QAAQ4F,KAAK;AAIf,MAAMsD,EAAM5I,EAASqF,KAAK;AAE1BrM,KAAK8D,eAAiB4H,KAAKS,MAAkC,IAA3BO,WAAWkD,EAAI,KAAO,IAAW,GACnE5P,KAAK+D,oBAAsB2H,KAAKS,MAAkC,IAA3BO,WAAWkD,EAAI,KAAO,IAAW,GACxE5P,KAAKgE,oBAAsB0H,KAAKS,MAAkC,IAA3BO,WAAWkD,EAAI,KAAO,IAAW,GACxE5P,KAAKiE,mBAAqByH,KAAKS,MAAkC,IAA3BO,WAAWkD,EAAI,KAAO,IAAW,EACzE,CAEAhG,yBAAAA,CAA0B5C,GACnBA,GAAaA,EAASqF,MAAiC,IAAzBrF,EAASqF,KAAKb,QAMjDxL,KAAKwE,mBAAqB,CACxBN,KAAM,CAAEC,QAAS,GACjBE,KAAM,CAAEF,QAAS,GACjBG,KAAM,CAAEH,QAAS,IAInB6C,EAASqF,KAAKwC,QAAQe,IACpB,MAAMS,EAAWX,SAASE,EAAI,IACxBU,EAAM5D,WAAWkD,EAAI,KAAO;AAEjB,IAAbS,EACFrQ,KAAKwE,mBAAmBN,KAAKC,QAAUuH,KAAKS,MAAY,GAANmE,GAAY,GACxC,IAAbD,EACTrQ,KAAKwE,mBAAmBH,KAAKF,QAAUuH,KAAKS,MAAY,GAANmE,GAAY,GACxC,IAAbD,IACTrQ,KAAKwE,mBAAmBF,KAAKH,QAAUuH,KAAKS,MAAY,GAANmE,GAAY,OArBhE5J,QAAQ4F,KAAK,qCAwBjB,CAEAiE,iBAAAA,CAAkBlJ,EAAMmJ,EAAOC,EAAQ,UAAWC,GAAY,GAC5D,MAAO,CACLtJ,KAAM,OACNC,KAAM,CACJ0D,OAAQ/K,KAAKsB,YACbqP,SAAU,CAAC,CACTH,QACAnJ,KAAMA,EAAKmF,IAAIoB,GAAKlC,KAAKS,MAAsB,GAAhBO,WAAWkB,IAAW,IACrDgD,gBAAiB,cACjBC,YAAaJ,EACbK,YAAa,EACbC,YAAa,EACbC,qBAAsBP,EACtBQ,iBAAkBR,EAClBS,QAAS,MAGbC,QAAS,CACPC,QAAS,CACPC,OAAQ,CACNC,SAAS,IAGbC,YAAY,EACZC,qBAAqB,EACrBC,YAAa,EACbC,OAAQ,CACNC,EAAG,CACDL,QAASZ,EACTkB,aAAa,EACbC,KAAM,CACJpB,MAAO,wBAGXqB,EAAG,CACDR,SAAS,EACTlK,KAAM,WACNyK,KAAM,CACJP,SAAS,GAEXS,MAAO,CACLC,YAAa,GACbC,YAAa,OAMzB,CAEAC,0BAAAA,CAA2B7K,EAAMsJ,EAAUD,GAAY,GACrD,MAAO,CACLtJ,KAAM,OACNC,KAAM,CACJ0D,OAAQ/K,KAAKsB,YACbqP,SAAUA,EAASnE,IAAI2F,KACrB3B,MAAO2B,EAAG3B,MACVnJ,KAAMA,EAAKmF,IAAIoB,GAAKlC,KAAKS,MAA8B,GAAxBO,WAAWkB,EAAEuE,EAAG7L,OAAc,IAC7DsK,gBAAiB,cACjBC,YAAasB,EAAG1B,MAChBK,YAAa,EACbC,YAAa,EACbC,qBAAsBmB,EAAG1B,MACzBQ,iBAAkBkB,EAAG1B,MACrBS,QAAS,MACLiB,EAAGC,YAAc,CAAEA,WAAYD,EAAGC,gBAG1CjB,QAAS,CACPC,QAAS,CACPC,OAAQ,CACNC,SAAS,EACTe,SAAU,QAGdd,YAAY,EACZC,qBAAqB,EACrBC,YAAa,EACbC,OAAQ,CACNC,EAAG,CACDL,QAASZ,EACTkB,aAAa,EACbC,KAAM,CACJpB,MAAO,wBAGXqB,EAAG,CACDR,SAAS,EACTlK,KAAM,WACNyK,KAAM,CACJP,SAAS,GAEXS,MAAO,CACLC,YAAa,GACbC,YAAa,OAMzB,CAEA,kBAAIK,GACF,IAAKtS,KAAKuB,aAAc,OAAO;AAE/B,IAAIiP,EAAQ;AASZ,MAR4B,QAAxBxQ,KAAKsC,eACPkO,EAAQ,mBACyB,QAAxBxQ,KAAKsC,eACdkO,EAAQ,4BACyB,YAAxBxQ,KAAKsC,iBACdkO,EAAQ,wBAGHxQ,KAAKuQ,kBAAkBvQ,KAAKuB,aAAciP,EAAO,UAC1D,CAEA,kBAAI+B,GACF,IAAKvS,KAAKwB,cAAe,OAAO;AAChC,MAAMgR,EAAUxS,KAAKwB,cAAcgL,IAAI,CAACoB,EAAGxN,KAAA,CACzC8D,KAAMlE,KAAKwB,cAAcpB,GACzBiE,KAAMrE,KAAKyB,cAAcrB,GACzBkE,KAAMtE,KAAK0B,cAActB;AAG3B,OAAOJ,KAAKkS,2BAA2BM,EAAS,CAC9C,CAAEhC,MAAO,QAASlK,IAAK,OAAQmK,MAAO,WACtC,CAAED,MAAO,QAASlK,IAAK,OAAQmK,MAAO,WACtC,CAAED,MAAO,SAAUlK,IAAK,OAAQmK,MAAO,YAE3C,CAEA,uBAAIgC,GACF,IAAKzS,KAAK2B,sBAAuB,OAAO;AAExC,MAAM+Q,EAAa1S,KAAK2B,sBAAsB6J,OACxCmH,EAAajH,KAAKC,IAAI,EAAG+G,EAAa,GAGtC/B,EAAW,CACf,CACEH,MAAO,QACPnJ,KAAMrH,KAAK2B,sBAAsB6K,IAAIoB,GAAKlC,KAAKS,MAAsB,GAAhBO,WAAWkB,IAAW,IAC3EgD,gBAAiB,cACjBC,YAAa,UACbC,YAAa,EACbC,YAAa,EACbC,qBAAsB,UACtBC,iBAAkB,UAClBC,QAAS,GACT0B,QAAS,CACPR,WAAaS,GACJA,EAAIC,aAAeH,EAAa,EAAI,CAAC,EAAG,GAAK,KAI1D,CACEnC,MAAO,QACPnJ,KAAMrH,KAAK4B,sBAAsB4K,IAAIoB,GAAKlC,KAAKS,MAAsB,GAAhBO,WAAWkB,IAAW,IAC3EgD,gBAAiB,cACjBC,YAAa,UACbC,YAAa,EACbC,YAAa,EACbC,qBAAsB,UACtBC,iBAAkB,UAClBC,QAAS,GACT0B,QAAS,CACPR,WAAaS,GACJA,EAAIC,aAAeH,EAAa,EAAI,CAAC,EAAG,GAAK,KAI1D,CACEnC,MAAO,SACPnJ,KAAMrH,KAAK6B,sBAAsB2K,IAAIoB,GAAKlC,KAAKS,MAAsB,GAAhBO,WAAWkB,IAAW,IAC3EgD,gBAAiB,cACjBC,YAAa,UACbC,YAAa,EACbC,YAAa,EACbC,qBAAsB,UACtBC,iBAAkB,UAClBC,QAAS,GACT0B,QAAS,CACPR,WAAaS,GACJA,EAAIC,aAAeH,EAAa,EAAI,CAAC,EAAG,GAAK;AAM5D,MAAO,CACLvL,KAAM,OACNC,KAAM,CACJ0D,OAAQ/K,KAAKsB,YACbqP,SAAUA,GAEZQ,QAAS,CACPC,QAAS,CACPC,OAAQ,CACNC,SAAS,EACTe,SAAU,QAGdd,YAAY,EACZC,qBAAqB,EACrBC,YAAa,EACbC,OAAQ,CACNC,EAAG,CACDL,SAAS,EACTM,aAAa,EACbC,KAAM,CACJpB,MAAO,wBAGXqB,EAAG,CACDR,SAAS,EACTlK,KAAM,WACNyK,KAAM,CACJP,SAAS,GAEXS,MAAO,CACLC,YAAa,GACbC,YAAa,OAMzB,CAEA,+BAAIc,GACF,OAAK/S,KAAK8B,2BACH9B,KAAKkS,2BAA2BlS,KAAK8B,2BAA4B,CACtE,CAAE0O,MAAO,oBAAqBlK,IAAK,YAAamK,MAAO,WACvD,CAAED,MAAO,gBAAiBlK,IAAK,QAASmK,MAAO,aAHJ,IAK/C,CAEA,wBAAIuC,GACF,OAAKhT,KAAK+B,qBACH/B,KAAKuQ,kBAAkBvQ,KAAK+B,qBAAsB,kBAAmB,WADrC,IAEzC,CAEA,qBAAIkR,GACF,OAAKjT,KAAKqC,sBAA6D,IAArCrC,KAAKqC,qBAAqBmJ,OACrDxL,KAAKuQ,kBAAkBvQ,KAAKqC,qBAAsB,oBAAqB,WADG,IAEnF,CAEA,4BAAI6Q,GACF,IAAKlT,KAAKmC,cAA6C,IAA7BnC,KAAKmC,aAAaqJ,OAAc,OAAO;AAEjE,MAAMgH,EAAUxS,KAAKmC,aAAaqK,IAAI,CAACoB,EAAGxN,KAAA,CACxC+S,IAAKnT,KAAKmC,aAAa/B,GACvBgT,QAASpT,KAAKoC,sBAAsBhC;AAGtC,OAAOJ,KAAKkS,2BAA2BM,EAAS,CAC9C,CAAEhC,MAAO,qBAAsBlK,IAAK,MAAOmK,MAAO,WAClD,CAAED,MAAO,sBAAuBlK,IAAK,UAAWmK,MAAO,YAE3D,CAGA4C,kBAAAA,CAAmBC,GACjB,MAAMC,EAASD,EAAMlP,OAAO4J;AAC5BhO,KAAKwT,aAAaD,EACpB,QAAAzT,OAAAqG,EAAAnG,KAAAC,gCAJCmG,GAAA,CAODoN,YAAAA,CAAaD,GACXvT,KAAKO,eAAiBgT,EACtBvT,KAAKmB,iBAAkB,EACvBnB,KAAKoB,UAAY,KACjBpB,KAAKqB,QAAU,KAEfrB,KAAKqD,WAAa,CAAA,EAElBrD,KAAKiB,cAAe,EACpBjB,KAAKU,YAAa,EAClBV,KAAKW,YAAa,EAClBX,KAAKkB,uBAAwB,EAC7BlB,KAAKY,iBAAkB,EACvBZ,KAAKa,yBAA0B,EAC/Bb,KAAKc,kBAAmB,EACxBd,KAAKe,cAAe,EACpBf,KAAKgB,iBAAkB,EACvBhB,KAAKiG,UACP,QAAAnG,OAAAqG,EAAAnG,KAAAC,0BAnBCmG,GAAA,CAsBDqN,gBAAAA,CAAiBC,GACf1T,KAAKsC,eAAiBoR,EACtB1T,KAAKmN,qBACP,QAAArN,OAAAqG,EAAAnG,KAAAC,8BAJCmG,GAAA,CAODuN,cAAAA,CAAeC,GACb5T,KAAKiC,iBAAmB2R,CAC1B,QAAA9T,OAAAqG,EAAAnG,KAAAC,4BAHCmG,GAAA,CAMDyN,eAAAA,GACE7T,KAAKkC,eAAyC,WAAxBlC,KAAKkC,eAA8B,SAAW,QACtE,QAAApC,OAAAqG,EAAAnG,KAAAC,6BAHCmG,GAAA,CAKD,qBAAI0N,GACF,IAAIC,EAAW/T,KAAKgC;AAGpB,GAA8B,QAA1BhC,KAAKiC,iBAA4B,CACnC,MAAM+R,EAAS,CAAE9P,KAAQ,EAAGG,KAAQ,EAAGC,KAAQ;AAC/CyP,EAAWA,EAASpG,OAAOsG,GAAOA,EAAI5D,WAAa2D,EAAOhU,KAAKiC,kBACjE,CASA,MANe,IAAI8R,GAAUjI,KAAK,CAACC,EAAGC,KACpC,MAAMkI,EAAQ,IAAIzL,KAAKsD,EAAEoE,YACnBgE,EAAQ,IAAI1L,KAAKuD,EAAEmE;AACzB,MAA+B,WAAxBnQ,KAAKkC,eAA8BgS,EAAQC,EAAQA,EAAQD,GAItE,CAGAE,iBAAAA,GACEpU,KAAKmB,iBAAmBnB,KAAKmB,gBACxBnB,KAAKmB,kBACRnB,KAAKoB,UAAY,KACjBpB,KAAKqB,QAAU,KAEfrB,KAAKqD,WAAa,CAAA,EAElBrD,KAAKiB,cAAe,EACpBjB,KAAKU,YAAa,EAClBV,KAAKW,YAAa,EAClBX,KAAKkB,uBAAwB,EAC7BlB,KAAKY,iBAAkB,EACvBZ,KAAKa,yBAA0B,EAC/Bb,KAAKc,kBAAmB,EACxBd,KAAKe,cAAe,EACpBf,KAAKgB,iBAAkB,EACvBhB,KAAKiG,WAET,QAAAnG,OAAAqG,EAAAnG,KAAAC,+BApBCmG,GAAA,CAuBDiO,eAAAA,CAAgBf,GACdtT,KAAKoB,UAAYkS,EAAMlP,OAAO4J,MAC1BhO,KAAKoB,WAAapB,KAAKqB,UAEzBrB,KAAKqD,WAAa,CAAA,EAElBrD,KAAKiB,cAAe,EACpBjB,KAAKU,YAAa,EAClBV,KAAKW,YAAa,EAClBX,KAAKkB,uBAAwB,EAC7BlB,KAAKY,iBAAkB,EACvBZ,KAAKa,yBAA0B,EAC/Bb,KAAKc,kBAAmB,EACxBd,KAAKe,cAAe,EACpBf,KAAKgB,iBAAkB,EACvBhB,KAAKiG,WAET,QAAAnG,OAAAqG,EAAAnG,KAAAC,6BAlBCmG,GAAA,CAqBDkO,aAAAA,CAAchB,GACZtT,KAAKqB,QAAUiS,EAAMlP,OAAO4J,MACxBhO,KAAKoB,WAAapB,KAAKqB,UAEzBrB,KAAKqD,WAAa,CAAA,EAElBrD,KAAKiB,cAAe,EACpBjB,KAAKU,YAAa,EAClBV,KAAKW,YAAa,EAClBX,KAAKkB,uBAAwB,EAC7BlB,KAAKY,iBAAkB,EACvBZ,KAAKa,yBAA0B,EAC/Bb,KAAKc,kBAAmB,EACxBd,KAAKe,cAAe,EACpBf,KAAKgB,iBAAkB,EACvBhB,KAAKiG,WAET,QAAAnG,OAAAqG,EAAAnG,KAAAC,2BAlBCmG,GAAA,CAoBD,kBAAImO,GACF,OAAIvU,KAAKoB,WAAapB,KAAKqB,QAClB,GAAG4G,OAAOjI,KAAKoB,WAAW8G,OAAO,oBAAoBD,OAAOjI,KAAKqB,SAAS6G,OAAO,iBAEnF,IACT,CAGAsM,cAAAA,CAAexG,GACb,OAAIA,EAAQ,GAAW,UACnBA,EAAQ,GAAW,UAChB,QACT,CAEAyG,mBAAAA,CAAoBzG,GAClB,OAAIA,EAAQ,GAAW,UACnBA,EAAQ,GAAW,UAChB,QACT,CAEA0G,uBAAAA,CAAwB1G,GACtB,OAAIA,EAAQ,GAAW,UACnBA,EAAQ,GAAW,UAChB,QACT,CAEA2G,sBAAAA,CAAuB3G,GACrB,OAAIA,GAAS,GAAW,UACpBA,EAAQ,GAAW,UAChB,QACT,CAEA,yBAAI4G,GACF,GAAI5U,KAAKmB,iBAAmBnB,KAAKoB,WAAapB,KAAKqB,QACjD,MAAO;AAWT,MARkB,CAChBiM,OAAU,kBACVuH,UAAa,mBACbC,MAAS,mBACT,UAAW,oBACX,WAAY,sBAGG9U,KAAKO,iBAAmB,qBAC3C,CAEA,mBAAIwU,GACF,GAAI/U,KAAKmB,gBACP,MAAO;AAWT,MARkB,CAChBmM,OAAU,KACVuH,UAAa,MACbC,MAAS,MACT,UAAW,MACX,WAAY,QAGG9U,KAAKO,iBAAmB,KAC3C,CAEA,wBAAIyU,GACF,MAAMpP,EAAgB5F,KAAKG,aAAa0F,QAAQC,KAAKC,GAAwB,gBAAfA,EAAMZ;AACpE,OAAOnF,KAAKG,cAAgBH,KAAKG,YAAY6F,OAASJ,EACxD,QAEAqP,EAAAC,EAAA,CAAArO,GAAA,KAAAsO,MAAA,i7yBAAAC,WAAA,4BAAAC,MAAAA,KAAAA,CAAAC,KAAAC,KAAAC,KAAAC,UAAAC,cAAA,IAieA1V,KAAU,ECr7EP,MAAC2V,EAAgB,CACpB;"}