postmortem/ui.go
2026-07-21 14:52:07 +03:00

170 lines
5.4 KiB
Go
Executable file
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
// ─── ANSI цвета ───────────────────────────────────────────────────────────────
const (
reset = "\033[0m"
bold = "\033[1m"
dim = "\033[2m"
red = "\033[31m"
green = "\033[32m"
yellow = "\033[33m"
blue = "\033[34m"
magenta = "\033[35m"
cyan = "\033[36m"
white = "\033[37m"
)
func clr(color, text string) string { return color + text + reset }
func bld(text string) string { return bold + text + reset }
// ─── Ввод ─────────────────────────────────────────────────────────────────────
var reader = bufio.NewReader(os.Stdin)
func readLine() string {
line, _ := reader.ReadString('\n')
return strings.TrimSpace(line)
}
// ask выводит подсказку и читает строку. Пустая строка → "_не указано_"
func ask(prompt string) string {
fmt.Printf(" %s %s ", clr(cyan, "→"), prompt)
val := readLine()
if val == "" {
return "_не указано_"
}
return val
}
// askDefault выводит подсказку с дефолтным значением.
func askDefault(prompt, defaultVal string) string {
fmt.Printf(" %s %s %s ", clr(cyan, "→"), prompt, clr(dim, "(Enter = "+defaultVal+")"))
val := readLine()
if val == "" {
return defaultVal
}
return val
}
// choose выводит нумерованное меню и возвращает выбранный вариант.
func choose(prompt string, options []string) string {
fmt.Printf("\n %s %s\n", clr(magenta, "▸"), bld(prompt))
for i, opt := range options {
fmt.Printf(" %s %s\n", clr(yellow, fmt.Sprintf("[%d]", i+1)), opt)
}
for {
fmt.Printf(" %s Выберите (1-%d): ", clr(cyan, "→"), len(options))
input := readLine()
n, err := strconv.Atoi(input)
if err == nil && n >= 1 && n <= len(options) {
fmt.Printf(" %s %s\n", clr(green, "✔"), options[n-1])
return options[n-1]
}
fmt.Printf(" %s Введите число от 1 до %d\n", clr(red, "✘"), len(options))
}
}
// section печатает визуальный разделитель с заголовком.
func section(title string) {
fmt.Printf("\n%s\n%s\n%s\n",
clr(blue, strings.Repeat("─", 60)),
clr(bold+blue, " "+title),
clr(blue, strings.Repeat("─", 60)),
)
}
// ─── Коллекторы списков ───────────────────────────────────────────────────────
func collectList(prompt, itemLabel string) []string {
fmt.Printf("\n %s %s %s\n", clr(magenta, "▸"), bld(prompt), clr(dim, "(пустая строка = завершить)"))
var items []string
for i := 1; ; i++ {
fmt.Printf(" %s %s %d: ", clr(cyan, "→"), itemLabel, i)
val := readLine()
if val == "" {
break
}
items = append(items, val)
}
if len(items) == 0 {
return []string{"_не указано_"}
}
return items
}
func collectTimeline() []TimelineEvent {
fmt.Printf("\n %s %s %s\n", clr(magenta, "▸"), bld("Хронология событий"), clr(dim, "(пустое время = завершить)"))
var events []TimelineEvent
for i := 1; ; i++ {
fmt.Printf(" %s Событие %d — время (напр. 14:32): ", clr(cyan, "→"), i)
t := readLine()
if t == "" {
break
}
fmt.Printf(" %s Событие %d — описание: ", clr(cyan, "→"), i)
desc := readLine()
if desc == "" {
desc = "—"
}
events = append(events, TimelineEvent{Time: t, Event: desc})
}
if len(events) == 0 {
events = append(events, TimelineEvent{Time: "—", Event: "_не указано_"})
}
return events
}
func collectWhyAnalysis() []WhyItem {
fmt.Printf("\n %s %s %s\n", clr(magenta, "▸"), bld("Анализ «5 Почему»"), clr(dim, "(пустой ввод = завершить, макс. 5)"))
var items []WhyItem
for i := 1; i <= 5; i++ {
fmt.Printf(" %s Почему #%d: ", clr(cyan, "→"), i)
why := readLine()
if why == "" {
break
}
fmt.Printf(" %s Ответ #%d: ", clr(cyan, "→"), i)
answer := readLine()
items = append(items, WhyItem{Why: why, Answer: answer})
}
if len(items) == 0 {
items = append(items, WhyItem{Why: "_не указано_", Answer: "—"})
}
return items
}
func collectActionItems() []ActionItem {
fmt.Printf("\n %s %s %s\n", clr(magenta, "▸"), bld("Action Items"), clr(dim, "(пустая задача = завершить)"))
var items []ActionItem
for i := 1; ; i++ {
fmt.Printf("\n %s Задача %d (Enter = завершить): ", clr(cyan, "→"), i)
task := readLine()
if task == "" {
break
}
fmt.Printf(" %s Ответственный (@имя): ", clr(cyan, "→"))
assignee := readLine()
if assignee == "" {
assignee = "@tbd"
}
fmt.Printf(" %s Дедлайн (ДД.ММ): ", clr(cyan, "→"))
deadline := readLine()
if deadline == "" {
deadline = "—"
}
items = append(items, ActionItem{Task: task, Assignee: assignee, Deadline: deadline})
}
if len(items) == 0 {
items = append(items, ActionItem{Task: "_не указано_", Assignee: "@tbd", Deadline: "—"})
}
return items
}