diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..6a3e68d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+**/.DS_Store
\ No newline at end of file
diff --git a/data/database.sql b/data/database.sql
old mode 100644
new mode 100755
diff --git a/data/fisch.db b/data/fisch.db
new file mode 100755
index 0000000..6e68a82
Binary files /dev/null and b/data/fisch.db differ
diff --git a/db.go b/db.go
new file mode 100755
index 0000000..b289d00
--- /dev/null
+++ b/db.go
@@ -0,0 +1,203 @@
+package main
+
+import (
+	"database/sql"
+	"os"
+
+	_ "github.com/mattn/go-sqlite3"
+)
+
+type Connection struct {
+	DB    *sql.DB
+	Error error
+}
+
+func Connect() *Connection {
+	db, err := sql.Open("sqlite3", "./data/fisch.db")
+	if err != nil {
+		return &Connection{nil, err}
+	}
+	return &Connection{db, nil}
+}
+
+func (conn *Connection) InitDatabase() error {
+	sql, err := os.ReadFile("./data/database.sql")
+	if err != nil {
+		return err
+	}
+	_, err = conn.DB.Exec(string(sql))
+	return err
+}
+
+func (conn *Connection) QueryLocations() ([]*Location, error) {
+	if conn.Error != nil {
+		return nil, conn.Error
+	}
+	rows, err := conn.DB.Query("select id, parent, name, description from locations")
+	if err != nil {
+		return nil, err
+	}
+
+	locations := make([]*Location, 0)
+
+	for rows.Next() {
+		location := Location{}
+
+		err := rows.Scan(&location.Id, &location.Parent, &location.Name, &location.Description)
+		if err != nil {
+			return nil, err
+		}
+
+		locations = append(locations, &location)
+	}
+
+	return locations, nil
+}
+
+func (conn *Connection) QueryLocation(id int64) (*Location, error) {
+	if conn.Error != nil {
+		return nil, conn.Error
+	}
+	row := conn.DB.QueryRow("select parent, name, description from locations where id=?", id)
+
+	location := Location{}
+	location.Id = sql.NullInt64{id, true}
+
+	err := row.Scan(&location.Parent, &location.Name, &location.Description)
+	if err != nil {
+		return nil, err
+	}
+
+	return &location, nil
+}
+
+func (conn *Connection) QueryContainers() ([]*Container, error) {
+	if conn.Error != nil {
+		return nil, conn.Error
+	}
+	rows, err := conn.DB.Query("select id, name from containers")
+	if err != nil {
+		return nil, err
+	}
+
+	containers := make([]*Container, 0)
+
+	for rows.Next() {
+		container := Container{}
+
+		err := rows.Scan(&container.Id, &container.Name)
+		if err != nil {
+			return nil, err
+		}
+
+		containers = append(containers, &container)
+	}
+
+	return containers, nil
+}
+
+func (conn *Connection) QueryContainer(id int64) (*Container, error) {
+	if conn.Error != nil {
+		return nil, conn.Error
+	}
+	row := conn.DB.QueryRow("select name from containers where id=?", id)
+
+	container := Container{}
+	container.Id = sql.NullInt64{id, true}
+
+	err := row.Scan(&container.Name)
+	if err != nil {
+		return nil, err
+	}
+
+	return &container, nil
+}
+
+func (conn *Connection) QueryParts() ([]*Part, error) {
+	if conn.Error != nil {
+		return nil, conn.Error
+	}
+	rows, err := conn.DB.Query("select id, name, tags, location, container from parts")
+	if err != nil {
+		return nil, err
+	}
+
+	parts := make([]*Part, 0)
+
+	for rows.Next() {
+		part := Part{}
+
+		var locationId sql.NullInt64
+		var containerId sql.NullInt64
+
+		err := rows.Scan(&part.Id, &part.Name, &part.Tags, &locationId, &containerId)
+		if err != nil {
+			return nil, err
+		}
+
+		var location *Location
+
+		if locationId.Valid {
+			location, err = conn.QueryLocation(locationId.Int64)
+			if err != nil {
+				return nil, err
+			}
+		}
+
+		var container *Container
+
+		if containerId.Valid {
+			container, err = conn.QueryContainer(containerId.Int64)
+			if err != nil {
+				return nil, err
+			}
+		}
+
+		part.Location = *location
+		part.Container = *container
+
+		parts = append(parts, &part)
+	}
+
+	return parts, nil
+}
+
+func (conn *Connection) QueryPart(id int64) (*Part, error) {
+	if conn.Error != nil {
+		return nil, conn.Error
+	}
+	row := conn.DB.QueryRow("select name, tags, location, container from parts where id=?", id)
+
+	part := Part{}
+	part.Id = sql.NullInt64{id, true}
+
+	var locationId sql.NullInt64
+	var containerId sql.NullInt64
+
+	err := row.Scan(&part.Name, &part.Tags, &locationId, &containerId)
+	if err != nil {
+		return nil, err
+	}
+
+	var location *Location
+
+	if locationId.Valid {
+		location, err = conn.QueryLocation(locationId.Int64)
+		if err != nil {
+			return nil, err
+		}
+		part.Location = *location
+	}
+
+	var container *Container
+
+	if containerId.Valid {
+		container, err = conn.QueryContainer(containerId.Int64)
+		if err != nil {
+			return nil, err
+		}
+		part.Container = *container
+	}
+
+	return &part, nil
+}
diff --git a/go.mod b/go.mod
old mode 100644
new mode 100755
index eee56cc..30c7739
--- a/go.mod
+++ b/go.mod
@@ -5,6 +5,7 @@ go 1.23.6
 require (
 	github.com/gofiber/fiber/v2 v2.52.6
 	github.com/gofiber/template/html/v2 v2.1.3
+	github.com/mattn/go-sqlite3 v1.14.24
 )
 
 require (
diff --git a/go.sum b/go.sum
old mode 100644
new mode 100755
index e0f9991..d02d605
--- a/go.sum
+++ b/go.sum
@@ -21,6 +21,8 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
 github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
 github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
+github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
diff --git a/main.go b/main.go
old mode 100644
new mode 100755
index 900c561..7c84c45
--- a/main.go
+++ b/main.go
@@ -21,6 +21,8 @@ func main() {
 		NewNavItem("Verwaltung", "/admin"),
 	}
 
+	conn := Connect()
+
 	app.Get("/", func(c *fiber.Ctx) error {
 		return c.Render("search", fiber.Map{
 			"Title":             "Suche",
@@ -54,14 +56,56 @@ func main() {
 	app.Get("/admin", func(c *fiber.Ctx) error {
 		return c.Render("admin/tables", fiber.Map{
 			"Title":      "Verwaltung",
-			"Stylenames": NewStyleItemList("colors", "main", "admin"),
+			"Stylenames": NewStyleItemList("colors", "main", "tables"),
+			"NavItems":   navItems,
+			"ActivePage": "/admin",
+			"Tables": []NavItem{
+				NewNavItem("Orte", "locations"),
+				NewNavItem("Behälter", "containers"),
+				NewNavItem("Teile", "parts"),
+			},
+		})
+	})
+
+	app.Get("/admin/locations/overview", func(c *fiber.Ctx) error {
+		locations, err := conn.QueryLocations()
+		if err != nil {
+			return err
+		}
+		table := ToTable[*Location](locations, TableColumns{
+			"ID",
+			"Übergeordneter Ort",
+			"Name",
+			"Beschreibung",
+		})
+
+		return c.Render("admin/overview", fiber.Map{
+			"Title":      "Verwaltung",
+			"Stylenames": NewStyleItemList("colors", "main", "overview"),
+			"NavItems":   navItems,
+			"ActivePage": "/admin",
+			"Table":      "locations",
+			"Columns":    table.Collumns,
+			"Rows":       table.Rows,
+		})
+	})
+
+	app.Get("/admin/containers/overview", func(c *fiber.Ctx) error {
+		return c.Render("admin/overview", fiber.Map{
+			"Title":      "Verwaltung",
+			"Stylenames": NewStyleItemList("colors", "main", "overview"),
 			"NavItems":   navItems,
 			"ActivePage": "/admin",
 		})
 	})
 
-	app.Get("/admin/locations/overview", func(c *fiber.Ctx) error {
-		return c.Render("admin/overview")
+	app.Get("/admin/parts/overview", func(c *fiber.Ctx) error {
+		return c.Render("admin/overview", fiber.Map{
+			"Title":      "Verwaltung",
+			"Stylenames": NewStyleItemList("colors", "main", "overview"),
+			"NavItems":   navItems,
+			"ActivePage": "/admin",
+		})
 	})
 
 	log.Fatal(app.Listen(":3000"))
diff --git a/misc.go b/misc.go
new file mode 100755
index 0000000..92230c2
--- /dev/null
+++ b/misc.go
@@ -0,0 +1,14 @@
+package main
+
+func ToTable[T DatabaseType](rows []T, columns TableColumns) Table {
+	tableRows := make([]TableRow, len(rows))
+
+	for i, dT := range rows {
+		tableRows[i] = dT.ToTableRow()
+	}
+
+	return Table{
+		columns,
+		tableRows,
+	}
+}
diff --git a/static/css/admin.css b/static/css/admin.css
deleted file mode 100644
index e69de29..0000000
diff --git a/static/css/colors.css b/static/css/colors.css
old mode 100644
new mode 100755
diff --git a/static/css/main.css b/static/css/main.css
old mode 100644
new mode 100755
diff --git a/static/css/overview.css b/static/css/overview.css
new file mode 100755
index 0000000..7e66fdd
--- /dev/null
+++ b/static/css/overview.css
@@ -0,0 +1,38 @@
+main {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  padding-top: 2em;
+  padding-bottom: 2em;
+}
+
+table {
+  border-collapse: collapse;
+  width: 1200px;
+  max-width: 100%;
+}
+
+table :is(th, td) {
+  border: 2px solid var(--bg-border);
+  padding: 1em;
+}
+
+table tr td:last-child > div.action-container {
+  display: flex;
+  align-items: baseline;
+  justify-content: space-around;
+}
+
+table tr td:last-child form {
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  height: max-content;
+}
+
+table tr td:last-child form button {
+  background-color: transparent;
+  border: none;
+  cursor: pointer;
+  color: var(--fg);
+}
\ No newline at end of file
diff --git a/static/css/search.css b/static/css/search.css
old mode 100644
new mode 100755
diff --git a/static/css/tables.css b/static/css/tables.css
new file mode 100755
index 0000000..bdd5a93
--- /dev/null
+++ b/static/css/tables.css
@@ -0,0 +1,15 @@
+main {
+    display: flex;
+    justify-content: center;
+    align-items: start;
+    gap: 1em;
+    padding: 1em;
+}
+
+main > a {
+    display: inline-block;
+    padding: 2em;
+    background-color: var(--bg-light);
+    border-radius: 50px;
+    font-size: 3em;
+}
\ No newline at end of file
diff --git a/types.go b/types.go
old mode 100644
new mode 100755
index a5ba263..c0dd8d4
--- a/types.go
+++ b/types.go
@@ -1,5 +1,10 @@
 package main
 
+import (
+	"database/sql"
+	"fmt"
+)
+
 // Web
 
 type NavItem struct {
@@ -17,15 +22,79 @@ func NewStyleItemList(stylenames ...Stylename) []Stylename {
 	return stylenames
 }
 
+type TableColumns []string
+
+type TableRow struct {
+	Columns TableColumns
+	Id      int
+}
+
+type Table struct {
+	Collumns TableColumns
+	Rows     []TableRow
+}
+
 // Database
 
 type Location struct {
-	id          int
-	parent      int
-	Name        string
-	Description string
+	Id          sql.NullInt64
+	Parent      sql.NullInt64
+	Name        sql.NullString
+	Description sql.NullString
+}
+
+func (l *Location) ToTableRow() TableRow {
+	columns := make(TableColumns, 4)
+
+	tr := TableRow{}
+
+	if l.Id.Valid {
+		columns[0] = fmt.Sprintf("%d", l.Id.Int64)
+		tr.Id = int(l.Id.Int64)
+	} else {
+		columns[0] = "NULL"
+		tr.Id = -1
+	}
+	if l.Parent.Valid {
+		columns[1] = fmt.Sprintf("%d", l.Parent.Int64)
+	} else {
+		columns[1] = "NULL"
+	}
+	if l.Name.Valid {
+		columns[2] = l.Name.String
+	} else {
+		columns[2] = "NULL"
+	}
+	if l.Description.Valid {
+		columns[3] = l.Description.String
+	} else {
+		columns[3] = "NULL"
+	}
+
+	tr.Columns = columns
+
+	return tr
 }
 
 func (l *Location) GetParent() *Location {
 	return nil
 }
+
+type Container struct {
+	Id   sql.NullInt64
+	Name sql.NullString
+}
+
+type Part struct {
+	Id        sql.NullInt64
+	Name      sql.NullString
+	Tags      sql.NullString
+	Location  Location
+	Container Container
+}
+
+// Interfaces
+
+type DatabaseType interface {
+	ToTableRow() TableRow
+}
diff --git a/views/admin/edit.html b/views/admin/edit.html
old mode 100644
new mode 100755
diff --git a/views/admin/overview.html b/views/admin/overview.html
old mode 100644
new mode 100755
index 409ca63..34477fd
--- a/views/admin/overview.html
+++ b/views/admin/overview.html
@@ -5,21 +5,25 @@
         {{ range .Columns }}
         <th>{{ . }}</th>
         {{ end }}
+        <th>Aktionen</th>
     </tr>
+    {{ $Table := .Table }}
     {{ range .Rows }}
     <tr>
         {{ range .Columns }}
         <td>{{ . }}</td>
         {{ end }}
         <td>
-            <form action="/admin/{{ .Table }}/edit">
-                <input type="hidden" name="id" value="{{ .Id }}">
-                <button type="submit">⛭</button>
-            </form>
-            <form action="/admin/{{ .Table }}/delete">
-                <input type="hidden" name="id" value="{{ .Id }}">
-                <button type="submit">⊖</button>
-            </form>
+            <div class="action-container">
+                <form action="/admin/{{ $Table }}/edit">
+                    <input type="hidden" name="id" value="{{ .Id }}">
+                    <button type="submit">⛭</button>
+                </form>
+                <form action="/admin/{{ $Table }}/delete">
+                    <input type="hidden" name="id" value="{{ .Id }}">
+                    <button type="submit">⊖</button>
+                </form>
+            </div>
         </td>
     </tr>
     {{ end }}
diff --git a/views/admin/tables.html b/views/admin/tables.html
old mode 100644
new mode 100755
index f46f18c..4ca86bc
--- a/views/admin/tables.html
+++ b/views/admin/tables.html
@@ -1,7 +1,7 @@
 {{template "partials/base-top" .}}
 
 {{ range .Tables }}
-<a href="/admin/{{ .Table }}/overview">{{ .Caption }}</a>
+<a href="/admin/{{ .Destination }}/overview">{{ .Caption }}</a>
 {{ end }}
 
 {{template "partials/base-bottom" .}}
\ No newline at end of file
diff --git a/views/alert.html b/views/alert.html
old mode 100644
new mode 100755
diff --git a/views/partials/base-bottom.html b/views/partials/base-bottom.html
old mode 100644
new mode 100755
index d3dcfd8..7f5a489
--- a/views/partials/base-bottom.html
+++ b/views/partials/base-bottom.html
@@ -1,7 +1,7 @@
     </main>
     <footer>
       <nav>
-        <a href="#">Repository</a>
+        <a href="https://git.ctdo.de/xoy/fisch">Repository</a>
       </nav>
     </footer>
   </body>
diff --git a/views/partials/base-top.html b/views/partials/base-top.html
old mode 100644
new mode 100755
index 1e5d351..b9f6036
--- a/views/partials/base-top.html
+++ b/views/partials/base-top.html
@@ -5,7 +5,7 @@
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title>fisch - {{ .Title }}</title>
     {{ range .Stylenames }}
-    <link rel="stylesheet" href="css/{{ . }}.css">
+    <link rel="stylesheet" href="/css/{{ . }}.css">
     {{ end }}
   </head>
   <body>
diff --git a/views/search.html b/views/search.html
old mode 100644
new mode 100755