Skip to main content

max / makenotwork

7.5 KB · 168 lines History Blame Raw
1 {%- import "partials/_ui.html" as ui -%}
2 {% if git_enabled %}
3 <div class="tab-docs"><a href="/docs/git">Docs: Git &rarr;</a> <a href="/dashboard#tab-ssh-keys" class="proj-code-docs-link">Manage SSH Keys &rarr;</a></div>
4
5 <div class="form-section" id="git-repos-section">
6 <h2 class="subsection-title">Git Repositories</h2>
7
8 {% if !linked_repos.is_empty() %}
9 <div class="proj-code-linked-list">
10 <label>Linked Repositories</label>
11 {% for repo in &linked_repos %}
12 <div class="proj-code-repo">
13 <div class="proj-code-repo-row">
14 <a href="/git/{{ repo.name }}" class="proj-code-repo-link">{{ repo.name }}</a>
15 <button class="btn-danger"
16 hx-delete="/api/projects/{{ project.id }}/repos/{{ repo.name }}"
17 hx-target="#git-repos-section"
18 hx-swap="outerHTML"
19 hx-confirm="Unlink '{{ repo.name }}' from this project?">Unlink</button>
20 </div>
21 {% if !repo.collaborators.is_empty() %}
22 <div class="proj-code-collab-block">
23 <label class="proj-code-collab-label">Collaborators</label>
24 {% for c in &repo.collaborators %}
25 <div class="proj-code-collab-row">
26 <span class="proj-code-collab-name">@{{ c.username }}{% if !c.can_push %} <span class="proj-code-collab-readonly">(read only)</span>{% endif %}</span>
27 <button class="btn-danger small"
28 hx-delete="/api/repos/{{ repo.id }}/collaborators/{{ c.user_id }}"
29 hx-target="#git-repos-section"
30 hx-swap="outerHTML"
31 hx-confirm="Remove @{{ c.username }} from {{ repo.name }}?">Remove</button>
32 </div>
33 {% endfor %}
34 </div>
35 {% endif %}
36 <div class="proj-code-collab-block">
37 <div class="proj-code-add-collab-row">
38 <input type="text" placeholder="Username" class="collab-username-input" data-repo-id="{{ repo.id }}">
39 <button class="btn-secondary small collab-add-btn" data-repo-id="{{ repo.id }}">Add Collaborator</button>
40 </div>
41 </div>
42 </div>
43 {% endfor %}
44 </div>
45 <script>
46 (function() {
47 document.querySelectorAll('.collab-add-btn').forEach(function(btn) {
48 btn.addEventListener('click', function() {
49 var repoId = btn.dataset.repoId;
50 var input = document.querySelector('.collab-username-input[data-repo-id="' + repoId + '"]');
51 var username = input.value.trim();
52 if (!username) return;
53 btn.disabled = true;
54 var body = new URLSearchParams();
55 body.append('username', username);
56 fetch('/api/repos/' + repoId + '/collaborators', {
57 method: 'POST',
58 headers: {...csrfHeaders(), 'Content-Type': 'application/x-www-form-urlencoded'},
59 body: body.toString()
60 }).then(function(r) {
61 if (r.ok) {
62 htmx.ajax('GET', '/dashboard/project/{{ project.slug }}/tabs/code', {target: '#tab-content', swap: 'innerHTML'});
63 } else {
64 return r.json().then(function(data) {
65 alert(data.error || 'Failed to add collaborator');
66 });
67 }
68 }).catch(function() {
69 alert('Network error');
70 }).finally(function() {
71 btn.disabled = false;
72 });
73 });
74 });
75 })();
76 </script>
77 {% endif %}
78
79 {% if !available_repos.is_empty() %}
80 <div>
81 <label for="link-repo-select">Link a Repository</label>
82 <div class="proj-code-link-row">
83 <select id="link-repo-select">
84 <option value="">Select a repository...</option>
85 {% for repo in &available_repos %}
86 <option value="{{ repo.name }}">{{ repo.name }}</option>
87 {% endfor %}
88 </select>
89 <button class="btn-secondary" id="link-repo-btn">Link</button>
90 </div>
91 <div class="hint">Choose from your unlinked repositories.</div>
92 </div>
93 <script>
94 (function() {
95 var btn = document.getElementById('link-repo-btn');
96 var sel = document.getElementById('link-repo-select');
97 if (!btn || !sel) return;
98 btn.addEventListener('click', function() {
99 var name = sel.value;
100 if (!name) return;
101 fetch('/api/projects/{{ project.id }}/repos', {
102 method: 'POST',
103 headers: {'Content-Type': 'application/json', ...csrfHeaders()},
104 body: JSON.stringify({name: name})
105 }).then(function() {
106 htmx.ajax('GET', '/dashboard/project/{{ project.slug }}/tabs/code', {target: '#tab-content', swap: 'innerHTML'});
107 }).catch(function() {
108 var status = document.getElementById('create-repo-status');
109 if (status) {
110 status.textContent = 'Failed to link repository. Please try again.';
111 status.classList.add('is-error');
112 }
113 });
114 });
115 })();
116 </script>
117 {% else if linked_repos.is_empty() %}
118 {% call ui::empty_state("", "No repositories available. Create one below or push to the server.") %}
119 {% endif %}
120
121 <div class="proj-code-create-section">
122 <label for="create-repo-name">Create Repository</label>
123 <div class="proj-code-link-row">
124 <input type="text" id="create-repo-name" placeholder="repo-name" pattern="[a-zA-Z0-9._-]+" title="Letters, numbers, hyphens, underscores, and dots">
125 <button class="btn-secondary" id="create-repo-btn">Create</button>
126 </div>
127 <div class="hint">Creates a bare git repository on the server.</div>
128 <div id="create-repo-status" class="proj-code-create-status"></div>
129 </div>
130 <script>
131 (function() {
132 var btn = document.getElementById('create-repo-btn');
133 var input = document.getElementById('create-repo-name');
134 var status = document.getElementById('create-repo-status');
135 if (!btn || !input) return;
136 btn.addEventListener('click', function() {
137 var name = input.value.trim();
138 if (!name) return;
139 btn.disabled = true;
140 fetch('/api/repos', {
141 method: 'POST',
142 headers: {'Content-Type': 'application/json', ...csrfHeaders()},
143 body: JSON.stringify({name: name})
144 }).then(function(r) {
145 if (r.ok) {
146 htmx.ajax('GET', '/dashboard/project/{{ project.slug }}/tabs/code', {target: '#tab-content', swap: 'innerHTML'});
147 } else {
148 return r.json().then(function(data) {
149 status.textContent = data.error || 'Failed to create repository';
150 status.classList.add('is-error');
151 });
152 }
153 }).catch(function() {
154 status.textContent = 'Network error';
155 status.classList.add('is-error');
156 }).finally(function() {
157 btn.disabled = false;
158 });
159 });
160 })();
161 </script>
162 </div>
163 {% else %}
164 <div class="proj-code-disabled">
165 <p>Git hosting is not configured on this server.</p>
166 </div>
167 {% endif %}
168