The first milestone finishes the contacts. For that you need almost everything from the modules before it at once: a rule in the core (modules 1.6 and 2.3), a table with a migration (2.4), a page without script (2.5), a permission decision (2.6) and tests on every level (2.7). Hardly anything is new. What’s new is that everything has to fit together in one piece.

The lesson follows the split from lesson 2.11.1:

Pull request Branch Brings Where in this lesson
1 feature/m1-company-model companies as entries of their own section “Pull request 1”
2 feature/m1-company-migration the company texts become companies section “Pull request 2”
3 feature/m1-tags tags exercise 1
4 feature/m1-search search, filter and pages section “Pull request 4”
5 feature/m1-delete deleting after a confirmation, for management only exercise 2

The acceptance criteria are in your issues from lesson 2.11.1. The test names in this lesson are the ones from the solution there.

Where the Kladde stands

In K1 the company was a text on the contact, and in most Kladdes it still is: a column Company in the table Contacts. That causes familiar problems. “Kranich Construction”, “kranich construction” and “Kranich Construction” with two spaces are three different texts, and a list of all contacts of one company finds only the ones written exactly the same.

The examples in this lesson start from this state:

  • Contact in Kladde.Core has Id, Name, Email (the value object EmailAddress) and the text Company.
  • The contact list /contacts searches and pages through the page’s address, with q for the search text and page for the page (lesson 2.5.4). The filter script from lesson 2.5.11 wraps form and list as a custom element.
  • DELETE /api/contacts/{id} has been reserved for management since lesson 2.6.3.
  • The roles are called Office and Management and have been constants since lesson 2.6.3: KladdeRoles.Office and KladdeRoles.Management in Kladde.Web.Auth.

The solutions of modules 2.4 to 2.6 are built further along or differently in a few places. Then carry the steps over like this:

  • Tags have been in the table ContactTags since lesson 2.4.6, one row per word. That stays; pull request 3 brings only the new rule. In the search, c.Tags.Contains(tag) then becomes db.ContactTags.Any(t => t.ContactId == c.Id && t.Tag == tag), and the projection reads the tags of a row as in lesson 2.4.9.
  • The email address is mapped by lesson 2.4.6 with a value converter; the examples here use a complex type with the same column Email. With the value converter, the search compares the whole address, like ContactDirectory in lesson 2.4.9; then leave the converter as it is, because the unique index UX_Contacts_Email sits on its column.
  • Search and pages ContactDirectory has had since lessons 2.4.9 and 2.5.4. Pull request 4 then extends that service with company and tag instead of putting a second one beside it. The page in pull request 4 shows only what is added for company and tag; the custom element of the filter script and the live region from lesson 2.5.11 stay in your page.
  • The confirmation for deleting at /contacts/{id}/delete has existed since exercise 3 of lesson 2.6.3, there with the policy KladdePolicies.Management. Exercise 2 adds what it still lacks.
  • A contact’s number is assigned in the solutions by the Kladde itself (ValueGeneratedNever, lesson 2.4.6), not by the database.

If your files, classes or folders are named differently, keep your names and carry the steps over.