Zadanie 13a. Aplikacja WPF do obsługi zleceń serwisowych iPhone

Założenia

  • dodawanie zleceń: klient, model, IMEI/SN, opis usterki, status, koszt, daty
  • lista zleceń z filtrem po roku, miesiącu i statusie
  • automatyczna „Suma miesiąca” (tylko naprawy zakończone w wybranym miesiącu)
  • zapis/odczyt do JSON w AppData użytkownika
  • walidacja „na żywo”, komendy, ICollectionView.Filter

1. Cel aplikacji

Napisz aplikację WPF „SerwisIphoneWpf”, która pozwala:

  • dodawać zlecenia serwisowe: Klient, Model, IMEI/SN, OpisUsterki, Status, Koszt (≥ 0), DataPrzyjecia, TerminDo, DataZakonczenia (opcjonalnie), CzyOplacone
  • przeglądać i filtrować listę po: Roku, Miesiącu (1–12) oraz Statusie
  • liczyć „Suma miesiąca” = suma Kosztów zleceń zakończonych w wskazanym roku i miesiącu
  • zapisać/wczytać listę do pliku JSON w katalogu danych użytkownika

2. Wymagania funkcjonalne

Formularz „Nowe zlecenie”:

  • Klient (wymagane, max 60), Model (lista: iPhone 8…iPhone 16 Pro/Max + „Inny”), IMEI/SN (wymagane, 8–20 znaków), Opis usterki (wymagane, max 400), Status (Przyjęte, W trakcie, Gotowe, Wydane), Koszt (liczba ≥ 0), DataPrzyjecia (DatePicker, wymagane), TerminDo (DatePicker, wymagane), DataZakonczenia (opcjonalnie), CzyOplacone (CheckBox).
  • „Dodaj” aktywny tylko przy poprawnych danych.

Lista (DataGrid) kolumny:

  • Klient, Model, IMEI/SN, Status, Koszt, Data przyjęcia, Termin do, Data zakończenia, Opłacone

Filtry:

  • Rok, Miesiąc (1–12), Status (Wszystkie / konkretne)
  • zmiana filtrów od razu odświeża widok i „Suma miesiąca”

Akcje:

  • Zapisz (JSON), Wczytaj (JSON), Usuń zaznaczone, Oznacz „Gotowe”, Oznacz „Opłacone”

Walidacja „na żywo” (czerwone obramowanie WPF):

  • Klient: wymagane, ≤ 60
  • Model: wymagany
  • IMEI/SN: wymagany, 8–20 znaków
  • OpisUsterki: wymagany, ≤ 400
  • Koszt: ≥ 0
  • DataPrzyjecia i TerminDo: wymagane; TerminDo ≥ DataPrzyjecia

3. Struktura projektu i nazewnictwo

SerwisIphoneWpf
├─ MainWindow.xaml
├─ MainWindow.xaml.cs
├─ Modele
│  └─ dane.cs                 // ZlecenieSerwisowe + walidacja (IDataErrorInfo)
├─ Walidacja
│  └─ WalidacjaSerwis.cs      // RelayCommand + helper walidacji
└─ Logika
   └─ LogikaSerwis.cs         // kolekcje/binding/filtrowanie/suma/zapis-odczyt

Nowe elementy:

  • IDataErrorInfo – źródło błędów walidacji dla WPF
  • RelayCommand – komendy do przycisków
  • ICollectionView.Filter – filtrowanie bez zmiany kolekcji

4. Interfejs – rozmieszczenie

Interfejs – opis rozmieszczenia

Główna siatka
4 wiersze: Auto, Auto, *, Auto i 2 kolumny: *, *.
Sekcje: GroupBox „Nowe zlecenie”, pasek filtrów i sumy, DataGrid, pasek akcji.

Sekcja „Nowe zlecenie” (GroupBox)
Grid wewnętrzny 3 wiersze (Auto) × 4 kolumny (2*, 2*, *, *). Pola:

  1. Klient
    TextBlock "Klient"
    TextBox x:Name="tbKlient"Text="{Binding Nowe.Klient, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"
    ToolTip: pierwszy komunikat z (Validation.Errors)[0].ErrorContent
  2. Model
    TextBlock "Model"
    ComboBox x:Name="cbModel"
    ItemsSource="{Binding Modele}"
    SelectedItem="{Binding Nowe.Model, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"
  3. IMEI / SN
    TextBlock "IMEI / SN"
    TextBox x:Name="tbImei" → binding jak wyżej
  4. Koszt (PLN)
    TextBlock "Koszt (PLN)"
    TextBox x:Name="tbKoszt"Text="{Binding Nowe.Koszt, ...}"
  5. Opis usterki
    TextBlock "Opis usterki"
    TextBox x:Name="tbOpis" AcceptsReturn="True" Height="80"Text="{Binding Nowe.OpisUsterki, ...}"
  6. Status
    TextBlock "Status"
    ComboBox x:Name="cbStatus"
    ItemsSource="{Binding Statusy}"
    SelectedItem="{Binding Nowe.Status, ...}"
  7. Opłacone
    CheckBox x:Name="chkOplacone"IsChecked="{Binding Nowe.CzyOplacone}"
  8. Daty
    DatePicker x:Name="dpPrzyjecie"SelectedDate="{Binding Nowe.DataPrzyjecia, ...}"
    DatePicker x:Name="dpTermin"SelectedDate="{Binding Nowe.TerminDo, ...}"
    DatePicker x:Name="dpZakonczenie"SelectedDate="{Binding Nowe.DataZakonczenia}" (opcjonalnie)

Przycisk
Button x:Name="btnDodaj" Content="Dodaj"
Command="{Binding DodajCmd}"
Aktywność przez RelayCommand.CanExecute (uaktualniane na Nowe.PropertyChanged)

Pasek filtrów i sumy
StackPanel (poziomy):

  • TextBox x:Name="tbRok"Text="{Binding Rok, UpdateSourceTrigger=PropertyChanged}"
  • TextBox x:Name="tbMiesiac"Text="{Binding Miesiac, UpdateSourceTrigger=PropertyChanged}"
  • ComboBox x:Name="cbStatusFiltra"
    ItemsSource="{Binding StatusFiltra}"
    SelectedItem="{Binding WybranyStatusFiltra}"
  • TextBlock x:Name="lblSuma"Text="{Binding SumaMiesiaca, StringFormat={}{0:F2} PLN}"
  • Button x:Name="btnZapisz"Command="{Binding ZapiszCmd}"
  • Button x:Name="btnWczytaj"Command="{Binding WczytajCmd}"

Lista zleceń
DataGrid x:Name="dgZlecenia"
ItemsSource="{Binding WidokZlecen}"
SelectedItem="{Binding Zaznaczone}"
Kolumny:

  • Klient{Binding Klient}
  • Model{Binding Model}
  • IMEI/SN{Binding ImeiSn}
  • Status{Binding Status}
  • Koszt{Binding Koszt, StringFormat={}{0:F2}}
  • Przyjęcie{Binding DataPrzyjecia, StringFormat={}{0:yyyy-MM-dd}}
  • Termin do{Binding TerminDo, StringFormat={}{0:yyyy-MM-dd}}
  • Zakończenie{Binding DataZakonczenia, StringFormat={}{0:yyyy-MM-dd}}
  • OpłaconeDataGridCheckBoxColumn {Binding CzyOplacone}

Pasek akcji (dół)
Po prawej:
btnUsunCommand="{Binding UsunCmd}"
btnGotoweCommand="{Binding OznaczGotoweCmd}"
btnOplaconeCommand="{Binding OznaczOplaconeCmd}"

Dodatkowe elementy „niewizualne”

MessageBox do komunikatów zapisu/odczytu Globalny styl walidacji dla TextBox, ComboBox, DatePicker (czerwony obrys i tooltip)

ICollectionView.Filter w LogikaSerwis steruje widokiem listy

5. Logika filtrowania i sumy (pseudokod)

Filtr(z):
  okRokMies = (Rok==0 || Miesiac==0) ? true
              : (z.DataPrzyjecia.Year==Rok || z.DataZakonczenia?.Year==Rok)
                && (z.DataPrzyjecia.Month==Miesiac || z.DataZakonczenia?.Month==Miesiac)
  okStatus = (WybranyStatus==Wszystkie) || (z.Status==WybranyStatus)
  return okRokMies && okStatus

PrzeliczSume():
  suma = 0
  dla z w Zlecenia:
    jeśli z.DataZakonczenia ma wartość
       i z.DataZakonczenia.Year == Rok
       i z.DataZakonczenia.Month == Miesiac:
         suma += z.Koszt
  zwróć suma

6. Kroki wykonania (dla ucznia)

  1. Utwórz projekt „SerwisIphoneWpf” (.NET 6/8, WPF App).
  2. Dodaj foldery i pliki jak w pkt 3.
  3. W MainWindow.xaml zbuduj layout z bindingami jak niżej.
  4. W Models/dane.cs zdefiniuj ZlecenieSerwisowe + IDataErrorInfo.
  5. W Walidacja/WalidacjaSerwis.cs dodaj RelayCommand.
  6. W Logika/LogikaSerwis.cs zaimplementuj kolekcję, widok, filtry, sumę i komendy.
  7. W MainWindow.xaml.cs ustaw DataContext = new LogikaSerwis();
  8. Uruchom i sprawdź: dodawanie, filtry, suma, zapis/odczyt, akcje.

7. Testy (co sprawdzi egzaminator)

  • Walidacja blokuje „Dodaj” przy błędach.
  • Zmiana Rok/Miesiąc/Status odświeża listę i sumę.
  • „Suma miesiąca” liczy tylko zlecenia zakończone w tym miesiącu.
  • „Usuń zaznaczone” działa i przelicza sumę.
  • Zapis tworzy JSON w AppData, Wczytaj odtwarza stan.
  • Oznaczanie „Gotowe” ustawia DataZakonczenia=Today (jeśli brak).
  • Oznaczanie „Opłacone” zaznacza CzyOplacone=true.

8. Kryteria oceniania (30 pkt)

  • Model + walidacja + IDataErrorInfo – 6 pkt
  • Bindingi (formularz/lista/suma/filtry) – 8 pkt
  • Komendy Dodaj/Usuń/Oznacz… – 6 pkt
  • Filtr + natychmiastowe odświeżanie + suma – 6 pkt
  • Zapis/odczyt JSON + ścieżka AppData – 4 pkt