Skip to content

Commit 662b9ee

Browse files
authored
Merge pull request #29 from BuildWithHussain/refactor-frontend-clean
Refactor frontend clean
2 parents 8f91367 + 8a8a496 commit 662b9ee

17 files changed

Lines changed: 1028 additions & 1050 deletions

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ jobs:
8383
- name: Setup
8484
run: |
8585
pip install frappe-bench
86-
bench init --skip-redis-config-generation --skip-assets --python "$(which python)" ~/frappe-bench
86+
bench init --skip-redis-config-generation --skip-assets --python "$(which python)" --frappe-branch version-15 ~/frappe-bench
8787
mariadb --host 127.0.0.1 --port 3306 -u root -proot -e "SET GLOBAL character_set_server = 'utf8mb4'"
8888
mariadb --host 127.0.0.1 --port 3306 -u root -proot -e "SET GLOBAL collation_server = 'utf8mb4_unicode_ci'"
8989

nano_press/templates/__base.html

Lines changed: 0 additions & 13 deletions
This file was deleted.

nano_press/templates/components/core/apps.html

Lines changed: 252 additions & 102 deletions
Large diffs are not rendered by default.
Lines changed: 106 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -1,153 +1,123 @@
1-
<div x-show="$store.installer.currentStep === 3" class="animate-fade-in" x-data="domainSetup()" x-init="init()">
2-
<h2 class="text-xl font-semibold mb-6 text-gray-900">Domain</h2>
3-
4-
<div class="mb-5">
5-
<label class="block text-sm font-medium mb-2 text-gray-900">Domain (Optional)</label>
6-
<input
7-
type="text"
8-
x-model="$store.installer.domain"
9-
name="domain"
10-
id="domain-input"
11-
placeholder="example.com"
12-
autocomplete="off"
13-
class="w-full px-3 py-2.5 bg-white border border-gray-300 rounded-md text-gray-900 outline-none transition-all duration-200 text-sm font-sans focus:border-gray-900"
14-
@input="updateDomain($event.target.value)"
15-
/>
16-
<p class="text-xs text-gray-400 mt-1.5">Leave empty to skip domain setup</p>
17-
18-
<!-- Domain Status Message -->
19-
<div x-show="domainStatusMessage" class="mt-2 text-sm" :class="$store.installer.domainVerified ? 'text-green-600' : 'text-red-600'">
20-
<span x-text="domainStatusMessage"></span>
1+
<div x-show="$store.installer.currentStep === 3" class="animate-fade-in" x-data="domainSetup()">
2+
<h2 class="text-xl font-semibold mb-3 text-gray-900">Add Domain</h2>
3+
4+
<p class="text-sm text-gray-600 mb-6">
5+
To add a custom domain, you must already own it. If you don't have one, skip this step and we'll provide you
6+
with a free Traefik domain.
7+
</p>
8+
9+
<div class="mb-5">
10+
<label class="block text-sm font-medium mb-2 text-gray-900">Domain (Optional)</label>
11+
<input type="text" x-model="$store.installer.domain" name="domain" id="domain-input" placeholder="example.com"
12+
autocomplete="off"
13+
class="w-full px-3 py-2.5 bg-white border border-gray-300 rounded-md text-gray-900 outline-none transition-all duration-200 text-sm font-sans focus:border-gray-900"
14+
@input="updateDomain($event.target.value)" />
15+
<p class="text-xs text-gray-400 mt-1.5">Leave empty to skip domain setup</p>
16+
17+
<div x-show="$store.installer.domain && $store.installer.domain.trim()" class="mt-3">
18+
<div class="bg-blue-50 border border-blue-200 rounded-md p-4">
19+
<p class="text-sm text-gray-700 mb-2">Point your domain to the server by creating an <span
20+
class="font-semibold">A record</span>:</p>
21+
<p class="font-mono text-sm text-gray-900 font-medium">
22+
<span x-text="$store.installer.domain"></span><span
23+
x-text="$store.installer.serverDetails?.ip || '0.0.0.0'"></span>
24+
</p>
25+
</div>
26+
</div>
27+
</div>
28+
29+
<div class="flex gap-3 mt-6">
30+
<button @click="prevStep()"
31+
class="flex-1 px-4 py-2.5 rounded-md font-medium border border-gray-300 cursor-pointer transition-all duration-200 text-sm bg-white text-gray-900 hover:bg-gray-50">
32+
Back
33+
</button>
34+
35+
<button @click="nextStep()" :disabled="isLoading"
36+
class="flex-1 px-4 py-2.5 rounded-md font-medium border border-gray-900 cursor-pointer transition-all duration-200 text-sm disabled:cursor-not-allowed inline-flex justify-center items-center gap-2 bg-gray-900 text-white hover:bg-gray-800 disabled:opacity-50">
37+
<svg x-show="isLoading" class="animate-spin h-5 w-5 text-gray-950" xmlns="http://www.w3.org/2000/svg"
38+
fill="none" viewBox="0 0 24 24">
39+
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
40+
<path class="opacity-75" fill="currentColor"
41+
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z">
42+
</path>
43+
</svg>
44+
<span
45+
x-text="isLoading ? 'Verifying...' : ($store.installer.domain && $store.installer.domain.trim() && !$store.installer.domainVerified ? 'Verify & Continue' : 'Continue')"></span>
46+
</button>
2147
</div>
22-
</div>
23-
24-
<div class="flex gap-3 mt-6">
25-
<button
26-
@click="prevStep()"
27-
class="flex-1 px-4 py-2.5 rounded-md font-medium border border-gray-300 cursor-pointer transition-all duration-200 text-sm bg-white text-gray-900 hover:bg-gray-50"
28-
>
29-
Back
30-
</button>
31-
32-
<!-- Dynamic Button -->
33-
<button
34-
@click="nextStep()"
35-
:disabled="isLoading"
36-
class="flex-1 px-4 py-2.5 rounded-md font-medium border border-gray-900 cursor-pointer transition-all duration-200 text-sm disabled:cursor-not-allowed inline-flex justify-center items-center gap-2 bg-gray-900 text-white hover:bg-gray-800 disabled:opacity-50"
37-
>
38-
<!-- Spinner -->
39-
<svg x-show="isLoading" class="animate-spin h-5 w-5 text-gray-950" xmlns="http://www.w3.org/2000/svg" fill="none"
40-
viewBox="0 0 24 24">
41-
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
42-
<path class="opacity-75" fill="currentColor"
43-
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z">
44-
</path>
45-
</svg>
46-
<span x-text="isLoading ? 'Verifying...' : ($store.installer.domain && $store.installer.domain.trim() && !$store.installer.domainVerified ? 'Verify & Continue' : 'Continue')"></span>
47-
</button>
48-
</div>
4948
</div>
5049

5150
<script>
52-
function domainSetup() {
53-
return {
54-
isLoading: false,
55-
domainStatusMessage: '',
56-
57-
// Initialize from store
58-
init() {
59-
if (this.$store.installer.domainVerified && this.$store.installer.domain) {
60-
this.domainStatusMessage = `✅ ${this.$store.installer.domain} is verified`;
61-
}
62-
},
63-
64-
updateDomain(value) {
65-
if (this.$store.installer.domainVerified && value !== this.$store.installer.domain) {
66-
this.$store.installer.domainVerified = false;
67-
this.domainStatusMessage = '';
68-
}
69-
this.$store.installer.domain = value;
70-
},
71-
72-
prevStep() {
73-
this.$store.installer.currentStep = 2;
74-
},
75-
76-
async nextStep() {
77-
const domain = this.$store.installer.domain;
78-
79-
if (domain && domain.trim()) {
80-
// Check if domain is already verified
81-
if (this.$store.installer.domainVerified) {
82-
this.domainStatusMessage = `✅ ${domain} is verified`;
83-
this.$store.installer.currentStep = 4;
84-
return;
51+
function domainSetup() {
52+
return {
53+
isLoading: false,
54+
55+
updateDomain(value) {
56+
if (this.$store.installer.domainVerified && value !== this.$store.installer.domain) {
57+
this.$store.installer.domainVerified = false;
8558
}
86-
87-
// Domain verification step
88-
this.isLoading = true;
89-
this.domainStatusMessage = '';
59+
this.$store.installer.domain = value;
60+
},
9061

62+
async verifyDomain(domain, expectedIp) {
63+
const url = '/api/method/nano_press.api.check_domain_resolves_to_ip';
9164
try {
92-
const expectedIp = this.$store.installer.serverDetails?.ip;
93-
94-
if (!expectedIp) {
95-
this.domainStatusMessage = "⚠️ Please enter a server IP address in the Server step to continue";
96-
if (typeof toast !== 'undefined') {
65+
const payload = {
66+
domain: domain,
67+
expected_ip: expectedIp
68+
};
69+
70+
const response = await frappe_call(url, payload, "POST");
71+
72+
if (!response?.message?.success) {
73+
throw new Error(response?.message?.message || "Domain verification failed");
74+
}
75+
76+
const message = response.message.message;
77+
toast(message, { type: "success" });
78+
return response.message;
79+
} catch (err) {
80+
const message = err.message || "Failed to verify domain";
81+
toast(message, { type: "danger" });
82+
throw err;
83+
}
84+
},
85+
86+
async nextStep() {
87+
const domain = this.$store.installer.domain;
88+
89+
if (domain && domain.trim()) {
90+
// Always verify domain - don't skip even if previously verified
91+
this.isLoading = true;
92+
93+
try {
94+
const expectedIp = this.$store.installer.serverDetails?.ip;
95+
96+
if (!expectedIp) {
9797
toast("Please enter a server IP address to continue", { type: "danger" });
98+
this.isLoading = false;
99+
return;
98100
}
101+
102+
await this.verifyDomain(domain, expectedIp);
103+
104+
this.$store.installer.domainVerified = true;
105+
106+
setTimeout(() => {
107+
this.$store.installer.currentStep++;
108+
}, 1000);
109+
} catch (err) {
110+
console.error('❌ Domain verification failed:', err);
111+
this.$store.installer.domainVerified = false;
112+
} finally {
99113
this.isLoading = false;
100-
return;
101114
}
102-
103-
await verify_domain(domain, expectedIp);
104-
105-
// Mark as verified in store
106-
this.$store.installer.domainVerified = true;
107-
this.domainStatusMessage = `✅ ${domain} resolves to ${expectedIp}`;
108-
109-
110-
setTimeout(() => {
111-
this.$store.installer.currentStep = 4;
112-
}, 1000);
113-
} catch (err) {
114-
console.error('❌ Domain verification failed:', err);
115+
} else {
116+
this.$store.installer.domain = '';
115117
this.$store.installer.domainVerified = false;
116-
this.domainStatusMessage = err.message || "Error verifying domain.";
117-
} finally {
118-
this.isLoading = false;
118+
this.$store.installer.currentStep++;
119119
}
120-
} else {
121-
// No domain, just continue
122-
this.$store.installer.domain = '';
123-
this.$store.installer.domainVerified = false;
124-
this.$store.installer.currentStep = 4;
125120
}
126-
}
127-
};
128-
}
129-
130-
async function verify_domain(domain, expectedIp) {
131-
const url = '/api/method/nano_press.api.check_domain_resolves_to_ip';
132-
try {
133-
const payload = {
134-
domain: domain,
135-
expected_ip: expectedIp
136121
};
137-
138-
const response = await frappe_call(url, payload, "POST");
139-
140-
if (!response?.message?.success) {
141-
throw new Error(response?.message?.message || "Domain verification failed");
142-
}
143-
144-
const message = response.message.message;
145-
toast(message, { type: "success" });
146-
return response.message;
147-
} catch (err) {
148-
const message = err.message || "Failed to verify domain";
149-
toast(message, { type: "danger" });
150-
throw err;
151122
}
152-
}
153123
</script>

0 commit comments

Comments
 (0)