Add project variants and UI updates

This commit is contained in:
Mikhail Chusavitin
2026-02-13 19:27:48 +03:00
parent 4e1a46bd71
commit 9b5d57902d
23 changed files with 1113 additions and 147 deletions

View File

@@ -200,6 +200,7 @@ func (s *Service) ImportProjectsToLocal() (*ProjectImportResult, error) {
}
existing.OwnerUsername = project.OwnerUsername
existing.Code = project.Code
existing.Name = project.Name
existing.TrackerURL = project.TrackerURL
existing.IsActive = project.IsActive
@@ -848,6 +849,12 @@ func (s *Service) pushProjectChange(change *localdb.PendingChange) error {
projectRepo := repository.NewProjectRepository(mariaDB)
project := payload.Snapshot
project.UUID = payload.ProjectUUID
if strings.TrimSpace(project.Code) == "" {
project.Code = strings.TrimSpace(derefString(project.Name))
if project.Code == "" {
project.Code = project.UUID
}
}
if err := projectRepo.UpsertByUUID(&project); err != nil {
return fmt.Errorf("upsert project on server: %w", err)
@@ -868,6 +875,17 @@ func (s *Service) pushProjectChange(change *localdb.PendingChange) error {
return nil
}
func derefString(value *string) string {
if value == nil {
return ""
}
return *value
}
func ptrString(value string) *string {
return &value
}
func decodeProjectChangePayload(change *localdb.PendingChange) (ProjectChangePayload, error) {
var payload ProjectChangePayload
if err := json.Unmarshal([]byte(change.Payload), &payload); err == nil && payload.ProjectUUID != "" {
@@ -1138,7 +1156,8 @@ func (s *Service) ensureConfigurationProject(mariaDB *gorm.DB, cfg *models.Confi
systemProject = &models.Project{
UUID: uuid.NewString(),
OwnerUsername: "",
Name: "Без проекта",
Code: "Без проекта",
Name: ptrString("Без проекта"),
IsActive: true,
IsSystem: true,
}
@@ -1302,6 +1321,21 @@ func (s *Service) loadCurrentConfigurationState(configurationUUID string) (model
}
}
if currentVersionNo == 0 {
if err := s.repairMissingConfigurationVersion(localCfg); err != nil {
return models.Configuration{}, "", 0, fmt.Errorf("repair missing configuration version: %w", err)
}
var latest localdb.LocalConfigurationVersion
err = s.localDB.DB().
Where("configuration_uuid = ?", configurationUUID).
Order("version_no DESC").
First(&latest).Error
if err == nil {
currentVersionNo = latest.VersionNo
currentVersionID = latest.ID
}
}
if currentVersionNo == 0 {
return models.Configuration{}, "", 0, fmt.Errorf("no local configuration version found for %s", configurationUUID)
}
@@ -1309,6 +1343,64 @@ func (s *Service) loadCurrentConfigurationState(configurationUUID string) (model
return cfg, currentVersionID, currentVersionNo, nil
}
func (s *Service) repairMissingConfigurationVersion(localCfg *localdb.LocalConfiguration) error {
if localCfg == nil {
return fmt.Errorf("local configuration is nil")
}
return s.localDB.DB().Transaction(func(tx *gorm.DB) error {
var cfg localdb.LocalConfiguration
if err := tx.Where("uuid = ?", localCfg.UUID).First(&cfg).Error; err != nil {
return fmt.Errorf("load local configuration: %w", err)
}
// If versions exist, just make sure current_version_id is set.
var latest localdb.LocalConfigurationVersion
if err := tx.Where("configuration_uuid = ?", cfg.UUID).
Order("version_no DESC").
First(&latest).Error; err == nil {
if cfg.CurrentVersionID == nil || *cfg.CurrentVersionID == "" {
if err := tx.Model(&localdb.LocalConfiguration{}).
Where("uuid = ?", cfg.UUID).
Update("current_version_id", latest.ID).Error; err != nil {
return fmt.Errorf("set current version id: %w", err)
}
}
return nil
} else if !errors.Is(err, gorm.ErrRecordNotFound) {
return fmt.Errorf("load latest version: %w", err)
}
snapshot, err := localdb.BuildConfigurationSnapshot(&cfg)
if err != nil {
return fmt.Errorf("build configuration snapshot: %w", err)
}
note := "Auto-repaired missing local version"
version := localdb.LocalConfigurationVersion{
ID: uuid.NewString(),
ConfigurationUUID: cfg.UUID,
VersionNo: 1,
Data: snapshot,
ChangeNote: &note,
AppVersion: appmeta.Version(),
CreatedAt: time.Now(),
}
if err := tx.Create(&version).Error; err != nil {
return fmt.Errorf("create initial version: %w", err)
}
if err := tx.Model(&localdb.LocalConfiguration{}).
Where("uuid = ?", cfg.UUID).
Update("current_version_id", version.ID).Error; err != nil {
return fmt.Errorf("set current version id: %w", err)
}
slog.Warn("repaired missing local configuration version", "uuid", cfg.UUID, "version_no", version.VersionNo)
return nil
})
}
// NOTE: prepared for future conflict resolution:
// when server starts storing version metadata, we can compare payload.CurrentVersionNo
// against remote version and branch into custom strategies. For now use last-write-wins.